Skip to content

Commit

Permalink
Expand state shim to offer to test for equality of branches states
Browse files Browse the repository at this point in the history
Support applications that need to check the equality of branches states less expensively: Expand the state shim framework to offer equality comparison between branches directly. Also, automate testing of this new functionality.
Apps used branch state serialization earlier to get equality-comparable representations. That approach was much more expensive.
  • Loading branch information
Viir committed Aug 12, 2023
1 parent bf8ebc8 commit 948adb3
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public record ListBranchesShimRequest
public record RemoveBranchesShimRequest(IReadOnlyList<string> BranchesNames)
: StateShimRequestStruct;

public record TestAreStatesEqualRequest(IReadOnlyList<StateSource> StatesSources)
: StateShimRequestStruct;

public string SerializeToJsonString() =>
JsonSerializer.Serialize(this);
}
Expand Down Expand Up @@ -60,6 +63,9 @@ public record ListBranchesShimResponse(IReadOnlyList<string> BranchesNames)

public record RemoveBranchesShimResponse(RemoveBranchesShimResponseStruct ResponseStruct)
: StateShimResponseStruct;

public record TestAreStatesEqualResponse(Result<string, bool> Result)
: StateShimResponseStruct;
}

public record ApplyFunctionShimRequestStruct(
Expand Down
45 changes: 39 additions & 6 deletions implement/elm-time/ElmTime/StateShim/StateShim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,18 @@ public static Result<string, IReadOnlyList<NamedExposedFunction>> ListExposedFun

public static Result<string, AdminInterface.ApplyDatabaseFunctionSuccess> ApplyFunctionOnMainBranch(
IProcess<string, string> process,
AdminInterface.ApplyDatabaseFunctionRequest request)
AdminInterface.ApplyDatabaseFunctionRequest request) =>
ApplyFunction(
process,
request: request,
stateSource: Maybe<StateSource>.just(MainStateBranch),
stateDestinationBranches:request.commitResultingState ? ImmutableList.Create(MainBranchName) : ImmutableList<string>.Empty);

public static Result<string, AdminInterface.ApplyDatabaseFunctionSuccess> ApplyFunction(
IProcess<string, string> process,
AdminInterface.ApplyDatabaseFunctionRequest request,
Maybe<StateSource> stateSource,
IReadOnlyList<string> stateDestinationBranches)
{
return
request.serializedArgumentsJson
Expand Down Expand Up @@ -110,12 +121,9 @@ public static Result<string, IReadOnlyList<NamedExposedFunction>> ListExposedFun
{
var stateArgument =
matchingFunction.functionDescription.parameters.Any(param => param.typeIsAppStateType) ?
Maybe<StateSource>.just(MainStateBranch) :
stateSource :
Maybe<StateSource>.nothing();
var stateDestinationBranches =
request.commitResultingState ? ImmutableList.Create(MainBranchName) : ImmutableList<string>.Empty;
var applyFunctionRequest =
new ApplyFunctionShimRequestStruct(
functionName: matchingFunction.functionName,
Expand Down Expand Up @@ -210,7 +218,6 @@ public static Result<string, string> SetBranchesState(
});
}


public static Result<string, RemoveBranchesShimResponseStruct> RemoveBranches(
IProcess<string, string> process,
IReadOnlyList<string> branches)
Expand All @@ -230,6 +237,32 @@ public static Result<string, RemoveBranchesShimResponseStruct> RemoveBranches(
});
}

public static Result<string, bool> TestAreBranchesEqual(
IProcess<string, string> process,
IReadOnlyList<string> branches) =>
TestAreStatesEqual(
process,
branches.Select(branch => new StateSource.BranchStateSource(branch)).ToImmutableList());

public static Result<string, bool> TestAreStatesEqual(
IProcess<string, string> process,
IReadOnlyList<StateSource> statesSources)
{
return
ProcessStateShimRequest(
process,
new StateShimRequestStruct.TestAreStatesEqualRequest(statesSources))
.AndThen(responseOk => responseOk.Response switch
{
StateShimResponseStruct.TestAreStatesEqualResponse testAreEqualResponse =>
testAreEqualResponse.Result,
_ =>
Result<string, bool>.err(
"Unexpected type of response: " + JsonSerializer.Serialize(responseOk))
});
}

public static Result<string, ProcessStateShimRequestReport> ProcessStateShimRequest(
IProcess<string, string> process,
StateShimRequestStruct stateShimRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,21 @@ processEvent config hostEvent stateBefore =
, RemoveBranchesShimResponse { removedCount = removedCount }
)
TestAreStatesEqualRequest statesSources ->
statesSources
|> List.map (resolveStateSource config stateBefore)
|> List.foldr (Result.map2 (::)) (Ok [])
|> Result.mapError ((++) "Failed to resolve state source: ")
|> Result.map (\\states ->
case states of
[] ->
True
first :: others ->
List.all ((==) first) others)
|> TestAreStatesEqualResponse
|> Tuple.pair stateBefore
setStateOnBranches : List String -> appState -> StateShimState appState -> StateShimState appState
setStateOnBranches branches appState stateBefore =
Expand Down Expand Up @@ -880,6 +895,7 @@ type StateShimRequest
| EstimateSerializedStateLengthShimRequest StateSource
| ListBranchesShimRequest
| RemoveBranchesShimRequest (List String)
| TestAreStatesEqualRequest (List StateSource)
type StateShimResponse
Expand All @@ -890,6 +906,7 @@ type StateShimResponse
| EstimateSerializedStateLengthShimResponse (Result String Int)
| ListBranchesShimResponse (List String)
| RemoveBranchesShimResponse { removedCount : Int }
| TestAreStatesEqualResponse (Result String Bool)
type alias ApplyFunctionShimRequestStruct =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,15 +495,9 @@ processBefore.lastElmAppVolatileProcess is null ?

var newElmAppProcess = prepareProcessResult.startProcess();

var applyInitSerialInterfaceResult = AttemptProcessRequest(
var applyInitSerialInterfaceResult = InitBranchesInElmInJsProcess(
newElmAppProcess,
new StateShim.InterfaceToHost.StateShimRequestStruct.ApplyFunctionShimRequest(
new StateShim.InterfaceToHost.ApplyFunctionShimRequestStruct(
functionName: "init",
arguments: new StateShim.InterfaceToHost.ApplyFunctionArguments<Maybe<StateShim.InterfaceToHost.StateSource>>(
stateArgument: Maybe<StateShim.InterfaceToHost.StateSource>.nothing(),
serializedArgumentsJson: ImmutableList<JsonElement>.Empty),
stateDestinationBranches: ImmutableList.Create("main"))));
stateDestinationBranches: ImmutableList.Create("main"));

return
applyInitSerialInterfaceResult
Expand Down Expand Up @@ -541,6 +535,19 @@ processBefore.lastElmAppVolatileProcess is null ?
"Unexpected shape of composition event: " + JsonSerializer.Serialize(compositionEvent));
}

public static Result<string, StateShim.InterfaceToHost.StateShimResponseStruct> InitBranchesInElmInJsProcess(
IProcessWithStringInterface elmInJsProcess,
IReadOnlyList<string> stateDestinationBranches) =>
AttemptProcessRequest(
elmInJsProcess,
new StateShim.InterfaceToHost.StateShimRequestStruct.ApplyFunctionShimRequest(
new StateShim.InterfaceToHost.ApplyFunctionShimRequestStruct(
functionName: "init",
arguments: new StateShim.InterfaceToHost.ApplyFunctionArguments<Maybe<StateShim.InterfaceToHost.StateSource>>(
stateArgument: Maybe<StateShim.InterfaceToHost.StateSource>.nothing(),
serializedArgumentsJson: ImmutableList<JsonElement>.Empty),
stateDestinationBranches: stateDestinationBranches)));

private record UpdateElmAppStateForEvent(
string functionName,
StateShim.InterfaceToHost.ApplyFunctionArguments<bool> arguments);
Expand Down
2 changes: 1 addition & 1 deletion implement/elm-time/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace ElmTime;

public class Program
{
public static string AppVersionId => "2023-08-10";
public static string AppVersionId => "2023-08-11";

private static int AdminInterfaceDefaultPort => 4000;

Expand Down
4 changes: 2 additions & 2 deletions implement/elm-time/elm-time.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>ElmTime</RootNamespace>
<AssemblyName>elm-time</AssemblyName>
<AssemblyVersion>2023.0810.0.0</AssemblyVersion>
<FileVersion>2023.0810.0.0</FileVersion>
<AssemblyVersion>2023.0811.0.0</AssemblyVersion>
<FileVersion>2023.0811.0.0</FileVersion>
<Nullable>enable</Nullable>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace TestElmTime;

[TestClass]
public class TestStateShim
public class StateShimTests
{
[TestMethod]
public void Test_state_shim_with_calculator_app()
Expand Down Expand Up @@ -89,6 +89,89 @@ public void Test_state_shim_with_calculator_app()
}
}

[TestMethod]
public void Test_state_shim_are_states_equal_with_calculator_app()
{
var deployment =
PineValueComposition.ParseAsTreeWithStringPath(TestElmWebAppHttpServer.CalculatorWebApp)
.Extract(err => throw new Exception(err.ToString()));

var preparedProcess =
ElmTime.Platform.WebService.PersistentProcessLiveRepresentation.ProcessFromDeployment(deployment);

using var calculatorProcess = preparedProcess.startProcess();

Result<string, ElmTime.AdminInterface.ApplyDatabaseFunctionSuccess> applyCalculatorOperationOnBranch(
string branchName,
CalculatorOperation calculatorOperation)
{
return
ElmTime.StateShim.StateShim.ApplyFunction(
calculatorProcess,
new ElmTime.AdminInterface.ApplyDatabaseFunctionRequest(
functionName: "Backend.ExposeFunctionsToAdmin.applyCalculatorOperation",
serializedArgumentsJson: ImmutableList.Create(
JsonSerializer.Serialize(calculatorOperation)),
commitResultingState: true),
stateSource: Maybe.NothingFromNull<StateSource>(new StateSource.BranchStateSource(branchName)),
stateDestinationBranches: ImmutableList.Create(branchName));
}

ElmTime.Platform.WebService.PersistentProcessLiveRepresentation.InitBranchesInElmInJsProcess(
calculatorProcess,
ImmutableList.Create("alfa", "beta"))
.Extract(err => throw new Exception(err));

Assert.IsTrue(
ElmTime.StateShim.StateShim.TestAreBranchesEqual(calculatorProcess, ImmutableList.Create("alfa", "beta"))
.Extract(err => throw new Exception(err)));

{
var applyOperationResult =
applyCalculatorOperationOnBranch(
"alfa",
new CalculatorOperation.AddOperation(1));

applyOperationResult.Extract(err => throw new Exception(err));
}

{
var applyOperationResult =
applyCalculatorOperationOnBranch(
"alfa",
new CalculatorOperation.AddOperation(3));

applyOperationResult.Extract(err => throw new Exception(err));
}

Assert.IsFalse(
ElmTime.StateShim.StateShim.TestAreBranchesEqual(calculatorProcess, ImmutableList.Create("alfa", "beta"))
.Extract(err => throw new Exception(err)));


{
var applyOperationResult =
applyCalculatorOperationOnBranch(
"beta",
new CalculatorOperation.AddOperation(5));

applyOperationResult.Extract(err => throw new Exception(err));
}

{
var applyOperationResult =
applyCalculatorOperationOnBranch(
"beta",
new CalculatorOperation.AddOperation(-1));

applyOperationResult.Extract(err => throw new Exception(err));
}

Assert.IsTrue(
ElmTime.StateShim.StateShim.TestAreBranchesEqual(calculatorProcess, ImmutableList.Create("alfa", "beta"))
.Extract(err => throw new Exception(err)));
}

[System.Text.Json.Serialization.JsonConverter(typeof(JsonConverterForChoiceType))]
private abstract record CalculatorOperation
{
Expand Down

0 comments on commit 948adb3

Please sign in to comment.