feat(inbound-api): ApiMethod.TraceAllCalls (entity, migration, mgmt command, CLI --trace-all-calls)
Flow Debugger (design 2026-09-05 §5.2): bit NOT NULL DEFAULT 0 column + EF migration 20260906034113 with its docs/plans/sql production twin. CreateApiMethodCommand gains TraceAllCalls=false; UpdateApiMethodCommand gains bool? TraceAllCalls (null = unchanged so an older CLI never resets it). Management result echoes the flag; CLI create/update expose --trace-all-calls; README carries the ~7-rows-per-call warning. Claude-Session: https://claude.ai/code/session_01GSLkkHKz9JrAtmJjmQK3cP
This commit is contained in:
@@ -0,0 +1,30 @@
|
|||||||
|
-- Run with QUOTED_IDENTIFIER ON (see Component-ConfigurationDatabase.md — every checked-in
|
||||||
|
-- script keeps this header in its own batch so the setting is in force when the following
|
||||||
|
-- batch parses; `sqlcmd` defaults it OFF).
|
||||||
|
SET QUOTED_IDENTIFIER ON;
|
||||||
|
SET ANSI_NULLS ON;
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- Flow Debugger (design 2026-09-05 §5.2): ApiMethod.TraceAllCalls — when 1, every
|
||||||
|
-- production call to the method is fully traced (~7 audit rows per call). Production
|
||||||
|
-- twin of EF migration 20260906034113_AddApiMethodTraceAllCalls.
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT * FROM [__EFMigrationsHistory]
|
||||||
|
WHERE [MigrationId] = N'20260906034113_AddApiMethodTraceAllCalls'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [ApiMethods] ADD [TraceAllCalls] bit NOT NULL DEFAULT CAST(0 AS bit);
|
||||||
|
END;
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT * FROM [__EFMigrationsHistory]
|
||||||
|
WHERE [MigrationId] = N'20260906034113_AddApiMethodTraceAllCalls'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||||
|
VALUES (N'20260906034113_AddApiMethodTraceAllCalls', N'10.0.7');
|
||||||
|
END;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
GO
|
||||||
@@ -60,6 +60,10 @@ public static class ApiMethodCommands
|
|||||||
timeoutOption.DefaultValueFactory = _ => 30;
|
timeoutOption.DefaultValueFactory = _ => 30;
|
||||||
var parametersOption = new Option<string?>("--parameters") { Description = "Parameter definitions JSON" };
|
var parametersOption = new Option<string?>("--parameters") { Description = "Parameter definitions JSON" };
|
||||||
var returnDefOption = new Option<string?>("--return-def") { Description = "Return type definition" };
|
var returnDefOption = new Option<string?>("--return-def") { Description = "Return type definition" };
|
||||||
|
var traceAllCallsOption = new Option<bool>("--trace-all-calls")
|
||||||
|
{
|
||||||
|
Description = "Trace every production call to this method for the Flow Debugger (adds ~7 audit rows per call; low-volume integration methods only)"
|
||||||
|
};
|
||||||
|
|
||||||
var cmd = new Command("create") { Description = "Create an API method" };
|
var cmd = new Command("create") { Description = "Create an API method" };
|
||||||
cmd.Add(nameOption);
|
cmd.Add(nameOption);
|
||||||
@@ -67,6 +71,7 @@ public static class ApiMethodCommands
|
|||||||
cmd.Add(timeoutOption);
|
cmd.Add(timeoutOption);
|
||||||
cmd.Add(parametersOption);
|
cmd.Add(parametersOption);
|
||||||
cmd.Add(returnDefOption);
|
cmd.Add(returnDefOption);
|
||||||
|
cmd.Add(traceAllCallsOption);
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
var name = result.GetValue(nameOption)!;
|
var name = result.GetValue(nameOption)!;
|
||||||
@@ -74,9 +79,10 @@ public static class ApiMethodCommands
|
|||||||
var timeout = result.GetValue(timeoutOption);
|
var timeout = result.GetValue(timeoutOption);
|
||||||
var parameters = result.GetValue(parametersOption);
|
var parameters = result.GetValue(parametersOption);
|
||||||
var returnDef = result.GetValue(returnDefOption);
|
var returnDef = result.GetValue(returnDefOption);
|
||||||
|
var traceAllCalls = result.GetValue(traceAllCallsOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, urlOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateApiMethodCommand(name, script, timeout, parameters, returnDef));
|
new CreateApiMethodCommand(name, script, timeout, parameters, returnDef, traceAllCalls));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
@@ -89,6 +95,11 @@ public static class ApiMethodCommands
|
|||||||
timeoutOption.DefaultValueFactory = _ => 30;
|
timeoutOption.DefaultValueFactory = _ => 30;
|
||||||
var parametersOption = new Option<string?>("--parameters") { Description = "Parameter definitions JSON" };
|
var parametersOption = new Option<string?>("--parameters") { Description = "Parameter definitions JSON" };
|
||||||
var returnDefOption = new Option<string?>("--return-def") { Description = "Return type definition" };
|
var returnDefOption = new Option<string?>("--return-def") { Description = "Return type definition" };
|
||||||
|
// bool? so an update that omits the flag leaves the stored value unchanged.
|
||||||
|
var traceAllCallsOption = new Option<bool?>("--trace-all-calls")
|
||||||
|
{
|
||||||
|
Description = "Trace every production call to this method for the Flow Debugger (true/false; omitted = unchanged; adds ~7 audit rows per call)"
|
||||||
|
};
|
||||||
|
|
||||||
var cmd = new Command("update") { Description = "Update an API method" };
|
var cmd = new Command("update") { Description = "Update an API method" };
|
||||||
cmd.Add(idOption);
|
cmd.Add(idOption);
|
||||||
@@ -96,6 +107,7 @@ public static class ApiMethodCommands
|
|||||||
cmd.Add(timeoutOption);
|
cmd.Add(timeoutOption);
|
||||||
cmd.Add(parametersOption);
|
cmd.Add(parametersOption);
|
||||||
cmd.Add(returnDefOption);
|
cmd.Add(returnDefOption);
|
||||||
|
cmd.Add(traceAllCallsOption);
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
@@ -103,9 +115,10 @@ public static class ApiMethodCommands
|
|||||||
var timeout = result.GetValue(timeoutOption);
|
var timeout = result.GetValue(timeoutOption);
|
||||||
var parameters = result.GetValue(parametersOption);
|
var parameters = result.GetValue(parametersOption);
|
||||||
var returnDef = result.GetValue(returnDefOption);
|
var returnDef = result.GetValue(returnDefOption);
|
||||||
|
var traceAllCalls = result.GetValue(traceAllCallsOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, urlOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateApiMethodCommand(id, script, timeout, parameters, returnDef));
|
new UpdateApiMethodCommand(id, script, timeout, parameters, returnDef, traceAllCalls));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2109,7 +2109,7 @@ scadabridge --url <url> api-method get --id <int>
|
|||||||
Create a new inbound API method.
|
Create a new inbound API method.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadabridge --url <url> api-method create --name <string> --script <string> [--timeout <int>] [--parameters <json>] [--return-def <json>]
|
scadabridge --url <url> api-method create --name <string> --script <string> [--timeout <int>] [--parameters <json>] [--return-def <json>] [--trace-all-calls]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Default | Description |
|
| Option | Required | Default | Description |
|
||||||
@@ -2119,6 +2119,7 @@ scadabridge --url <url> api-method create --name <string> --script <string> [--t
|
|||||||
| `--timeout` | no | `30` | Timeout in seconds |
|
| `--timeout` | no | `30` | Timeout in seconds |
|
||||||
| `--parameters` | no | — | Parameter definitions JSON |
|
| `--parameters` | no | — | Parameter definitions JSON |
|
||||||
| `--return-def` | no | — | Return type definition JSON |
|
| `--return-def` | no | — | Return type definition JSON |
|
||||||
|
| `--trace-all-calls` | no | off | Trace every production call for the Flow Debugger. **Adds ~7 audit rows per call** — low-volume integration methods only |
|
||||||
|
|
||||||
#### `api-method update`
|
#### `api-method update`
|
||||||
|
|
||||||
@@ -2126,7 +2127,7 @@ Update an inbound API method. An update **replaces** the whole entity — every
|
|||||||
field below must be supplied, even if unchanged.
|
field below must be supplied, even if unchanged.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadabridge --url <url> api-method update --id <int> --script <string> [--timeout <int>] [--parameters <json>] [--return-def <json>]
|
scadabridge --url <url> api-method update --id <int> --script <string> [--timeout <int>] [--parameters <json>] [--return-def <json>] [--trace-all-calls <true|false>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Default | Description |
|
| Option | Required | Default | Description |
|
||||||
@@ -2136,6 +2137,7 @@ scadabridge --url <url> api-method update --id <int> --script <string> [--timeou
|
|||||||
| `--timeout` | no | `30` | Timeout in seconds |
|
| `--timeout` | no | `30` | Timeout in seconds |
|
||||||
| `--parameters` | no | — | Parameter definitions JSON |
|
| `--parameters` | no | — | Parameter definitions JSON |
|
||||||
| `--return-def` | no | — | Return type definition JSON |
|
| `--return-def` | no | — | Return type definition JSON |
|
||||||
|
| `--trace-all-calls` | no | unchanged | `true`/`false` — trace every production call for the Flow Debugger. **Adds ~7 audit rows per call.** Omitted = the stored value is left as-is (the one field the replace-all rule does not apply to) |
|
||||||
|
|
||||||
Script changes take effect immediately — the updated code is recompiled in-memory on the active central node. No restart is required.
|
Script changes take effect immediately — the updated code is recompiled in-memory on the active central node. No restart is required.
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ public class ApiMethod
|
|||||||
/// <summary>Gets or sets the script execution timeout in seconds.</summary>
|
/// <summary>Gets or sets the script execution timeout in seconds.</summary>
|
||||||
public int TimeoutSeconds { get; set; }
|
public int TimeoutSeconds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When true every production call to this method is fully traced (Flow Debugger design
|
||||||
|
/// 2026-09-05 §5.2); intended for low-volume integration methods — ~7 audit rows per call.
|
||||||
|
/// </summary>
|
||||||
|
public bool TraceAllCalls { get; set; }
|
||||||
|
|
||||||
/// <summary>Initializes a new <see cref="ApiMethod"/> with the required name and script.</summary>
|
/// <summary>Initializes a new <see cref="ApiMethod"/> with the required name and script.</summary>
|
||||||
/// <param name="name">The method name (used as the route segment).</param>
|
/// <param name="name">The method name (used as the route segment).</param>
|
||||||
/// <param name="script">The C# script body to execute.</param>
|
/// <param name="script">The C# script body to execute.</param>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
|||||||
|
|
||||||
public record ListApiMethodsCommand;
|
public record ListApiMethodsCommand;
|
||||||
public record GetApiMethodCommand(int ApiMethodId);
|
public record GetApiMethodCommand(int ApiMethodId);
|
||||||
public record CreateApiMethodCommand(string Name, string Script, int TimeoutSeconds, string? ParameterDefinitions, string? ReturnDefinition);
|
/// <param name="TraceAllCalls">Flow Debugger (design 2026-09-05 §5.2): trace every production call to this method. Additive; defaults to off.</param>
|
||||||
public record UpdateApiMethodCommand(int ApiMethodId, string Script, int TimeoutSeconds, string? ParameterDefinitions, string? ReturnDefinition);
|
public record CreateApiMethodCommand(string Name, string Script, int TimeoutSeconds, string? ParameterDefinitions, string? ReturnDefinition, bool TraceAllCalls = false);
|
||||||
|
/// <param name="TraceAllCalls">Flow Debugger (design 2026-09-05 §5.2): <c>null</c> leaves the stored value unchanged so an older CLI never resets it.</param>
|
||||||
|
public record UpdateApiMethodCommand(int ApiMethodId, string Script, int TimeoutSeconds, string? ParameterDefinitions, string? ReturnDefinition, bool? TraceAllCalls = null);
|
||||||
public record DeleteApiMethodCommand(int ApiMethodId);
|
public record DeleteApiMethodCommand(int ApiMethodId);
|
||||||
|
|||||||
+5
@@ -31,6 +31,11 @@ public class ApiMethodConfiguration : IEntityTypeConfiguration<ApiMethod>
|
|||||||
builder.Property(m => m.ReturnDefinition)
|
builder.Property(m => m.ReturnDefinition)
|
||||||
.HasMaxLength(4000);
|
.HasMaxLength(4000);
|
||||||
|
|
||||||
|
// Flow Debugger (design 2026-09-05 §5.2): bit NOT NULL DEFAULT 0.
|
||||||
|
builder.Property(m => m.TraceAllCalls)
|
||||||
|
.IsRequired()
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
builder.HasIndex(m => m.Name).IsUnique();
|
builder.HasIndex(m => m.Name).IsUnique();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2136
File diff suppressed because it is too large
Load Diff
+29
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddApiMethodTraceAllCalls : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "TraceAllCalls",
|
||||||
|
table: "ApiMethods",
|
||||||
|
type: "bit",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TraceAllCalls",
|
||||||
|
table: "ApiMethods");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -511,6 +511,11 @@ namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Migrations
|
|||||||
b.Property<int>("TimeoutSeconds")
|
b.Property<int>("TimeoutSeconds")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<bool>("TraceAllCalls")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("Name")
|
b.HasIndex("Name")
|
||||||
|
|||||||
@@ -3447,7 +3447,8 @@ public class ManagementActor : ReceiveActor
|
|||||||
{
|
{
|
||||||
TimeoutSeconds = cmd.TimeoutSeconds,
|
TimeoutSeconds = cmd.TimeoutSeconds,
|
||||||
ParameterDefinitions = cmd.ParameterDefinitions,
|
ParameterDefinitions = cmd.ParameterDefinitions,
|
||||||
ReturnDefinition = cmd.ReturnDefinition
|
ReturnDefinition = cmd.ReturnDefinition,
|
||||||
|
TraceAllCalls = cmd.TraceAllCalls
|
||||||
};
|
};
|
||||||
await repo.AddApiMethodAsync(method);
|
await repo.AddApiMethodAsync(method);
|
||||||
await repo.SaveChangesAsync();
|
await repo.SaveChangesAsync();
|
||||||
@@ -3468,6 +3469,8 @@ public class ManagementActor : ReceiveActor
|
|||||||
method.TimeoutSeconds = cmd.TimeoutSeconds;
|
method.TimeoutSeconds = cmd.TimeoutSeconds;
|
||||||
method.ParameterDefinitions = cmd.ParameterDefinitions;
|
method.ParameterDefinitions = cmd.ParameterDefinitions;
|
||||||
method.ReturnDefinition = cmd.ReturnDefinition;
|
method.ReturnDefinition = cmd.ReturnDefinition;
|
||||||
|
if (cmd.TraceAllCalls is { } traceAllCalls)
|
||||||
|
method.TraceAllCalls = traceAllCalls;
|
||||||
await repo.UpdateApiMethodAsync(method);
|
await repo.UpdateApiMethodAsync(method);
|
||||||
await repo.SaveChangesAsync();
|
await repo.SaveChangesAsync();
|
||||||
// The persisted DB row is authoritative: on a compile failure the method returns
|
// The persisted DB row is authoritative: on a compile failure the method returns
|
||||||
@@ -3531,6 +3534,7 @@ public class ManagementActor : ReceiveActor
|
|||||||
method.ParameterDefinitions,
|
method.ParameterDefinitions,
|
||||||
method.ReturnDefinition,
|
method.ReturnDefinition,
|
||||||
method.TimeoutSeconds,
|
method.TimeoutSeconds,
|
||||||
|
method.TraceAllCalls,
|
||||||
compileWarning,
|
compileWarning,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
using Akka.Actor;
|
||||||
|
using Akka.TestKit.Xunit2;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using NSubstitute;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||||
|
|
||||||
|
namespace ZB.MOM.WW.ScadaBridge.ManagementService.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flow Debugger design 2026-09-05 §5.2: <c>ApiMethod.TraceAllCalls</c> round-trips through the
|
||||||
|
/// Create/Update management commands, defaults to off, and an Update that omits it (an older
|
||||||
|
/// CLI) leaves the stored value unchanged.
|
||||||
|
/// </summary>
|
||||||
|
public class ApiMethodTraceAllCallsTests : TestKit, IDisposable
|
||||||
|
{
|
||||||
|
private readonly ServiceCollection _services = new();
|
||||||
|
private readonly IInboundApiRepository _apiRepo = Substitute.For<IInboundApiRepository>();
|
||||||
|
|
||||||
|
public ApiMethodTraceAllCallsTests()
|
||||||
|
{
|
||||||
|
_services.AddScoped(_ => _apiRepo);
|
||||||
|
_services.AddScoped(_ => Substitute.For<IAuditService>());
|
||||||
|
_apiRepo.SaveChangesAsync(Arg.Any<CancellationToken>()).Returns(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IActorRef CreateActor() =>
|
||||||
|
Sys.ActorOf(Props.Create(() => new ManagementActor(
|
||||||
|
_services.BuildServiceProvider(), NullLogger<ManagementActor>.Instance)));
|
||||||
|
|
||||||
|
private static ManagementEnvelope Envelope(object command) =>
|
||||||
|
new(new AuthenticatedUser("designer", "Designer", new[] { "Designer" }, Array.Empty<string>()),
|
||||||
|
command, Guid.NewGuid().ToString("N"));
|
||||||
|
|
||||||
|
void IDisposable.Dispose() => Shutdown();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Commands_DefaultToUntraced_ForTheFiveArgConstructor()
|
||||||
|
{
|
||||||
|
Assert.False(new CreateApiMethodCommand("m", "return 1;", 30, null, null).TraceAllCalls);
|
||||||
|
Assert.Null(new UpdateApiMethodCommand(1, "return 1;", 30, null, null).TraceAllCalls);
|
||||||
|
Assert.False(new ApiMethod("m", "return 1;").TraceAllCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_WithTraceAllCalls_PersistsTheFlag_AndEchoesItInTheResult()
|
||||||
|
{
|
||||||
|
ApiMethod? added = null;
|
||||||
|
_apiRepo.AddApiMethodAsync(Arg.Do<ApiMethod>(m => added = m), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
CreateActor().Tell(Envelope(new CreateApiMethodCommand("Traced", "return 1;", 30, null, null, TraceAllCalls: true)));
|
||||||
|
|
||||||
|
var response = ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(10));
|
||||||
|
Assert.NotNull(added);
|
||||||
|
Assert.True(added!.TraceAllCalls);
|
||||||
|
Assert.Contains("\"traceAllCalls\":true", response.JsonData.Replace(" ", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Update_WithExplicitFalse_ClearsTheFlag()
|
||||||
|
{
|
||||||
|
var existing = new ApiMethod("M", "return 1;") { Id = 7, TimeoutSeconds = 10, TraceAllCalls = true };
|
||||||
|
_apiRepo.GetApiMethodByIdAsync(7, Arg.Any<CancellationToken>()).Returns(existing);
|
||||||
|
|
||||||
|
CreateActor().Tell(Envelope(new UpdateApiMethodCommand(7, "return 2;", 10, null, null, TraceAllCalls: false)));
|
||||||
|
|
||||||
|
ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(10));
|
||||||
|
Assert.False(existing.TraceAllCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Update_OmittingTheFlag_LeavesTheStoredValueUnchanged()
|
||||||
|
{
|
||||||
|
var existing = new ApiMethod("M", "return 1;") { Id = 7, TimeoutSeconds = 10, TraceAllCalls = true };
|
||||||
|
_apiRepo.GetApiMethodByIdAsync(7, Arg.Any<CancellationToken>()).Returns(existing);
|
||||||
|
|
||||||
|
// The 5-arg shape an older CLI sends: TraceAllCalls == null.
|
||||||
|
CreateActor().Tell(Envelope(new UpdateApiMethodCommand(7, "return 2;", 10, null, null)));
|
||||||
|
|
||||||
|
ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(10));
|
||||||
|
Assert.True(existing.TraceAllCalls);
|
||||||
|
Assert.Equal("return 2;", existing.Script);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user