Add --inline-accessors CLI flag, layered dispatcher regression goldens, and accessor index docs.
Expose accessor index toggling through ExportService and picocli, lock in layered_dispatcher_sample exports as golden fixtures, and document the static analysis pipeline in the README. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -155,6 +155,18 @@ public class RegressionTest {
|
||||
root.resolve("state_machines/polymorphic_events_sample"),
|
||||
Path.of("src/test/resources/golden/PolymorphicStateMachineConfiguration"),
|
||||
"PolymorphicStateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Layered Dispatcher (Order)",
|
||||
root.resolve("state_machines/layered_dispatcher_sample"),
|
||||
Path.of("src/test/resources/golden/StandardOrderStateMachineConfiguration"),
|
||||
"StandardOrderStateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Layered Dispatcher (Document)",
|
||||
root.resolve("state_machines/layered_dispatcher_sample"),
|
||||
Path.of("src/test/resources/golden/StandardDocumentStateMachineConfiguration"),
|
||||
"StandardDocumentStateMachineConfiguration"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package click.kamil.springstatemachineexporter.service;
|
||||
|
||||
import click.kamil.springstatemachineexporter.exporter.JsonExporter;
|
||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class ExportServiceInlineAccessorsTest {
|
||||
|
||||
@Test
|
||||
void shouldSkipAccessorIndexWhenInlineAccessorsDisabled(@TempDir Path tempDir) throws IOException {
|
||||
Path sourceRoot = tempDir.resolve("project");
|
||||
writeJava(sourceRoot, "com/example/Payload.java", """
|
||||
package com.example;
|
||||
public class Payload {
|
||||
private String event = "PAY";
|
||||
public String getEvent() { return event; }
|
||||
}
|
||||
""");
|
||||
writeJava(sourceRoot, "com/example/Config.java", """
|
||||
package com.example;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public class Config extends StateMachineConfigurerAdapter<String, String> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
|
||||
states.withStates().initial("NEW").state("PAID");
|
||||
}
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
|
||||
transitions.withExternal().source("NEW").target("PAID").event("PAY");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
CodebaseContext enabledContext = scanContext(sourceRoot, true);
|
||||
assertThat(enabledContext.getAccessorIndex().trivialCount()).isGreaterThan(0);
|
||||
|
||||
CodebaseContext disabledContext = scanContext(sourceRoot, false);
|
||||
assertThat(disabledContext.getAccessorIndex().trivialCount()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExportWithInlineAccessorsFlag(@TempDir Path inputDir, @TempDir Path outputDir) throws IOException {
|
||||
writeJava(inputDir, "com/example/SimpleConfig.java", """
|
||||
package com.example;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public class SimpleConfig extends StateMachineConfigurerAdapter<String, String> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
|
||||
states.withStates().initial("A").state("B");
|
||||
}
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
|
||||
transitions.withExternal().source("A").target("B").event("GO");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
ExportService exportService = new ExportService(List.of(new JsonExporter()));
|
||||
exportService.runExporter(inputDir, outputDir, List.of("json"), true, List.of(), null, null,
|
||||
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn,
|
||||
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn,
|
||||
true,
|
||||
false);
|
||||
|
||||
try (var stream = Files.list(outputDir)) {
|
||||
assertThat(stream.filter(Files::isDirectory).count()).isGreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static CodebaseContext scanContext(Path sourceRoot, boolean inlineAccessors) throws IOException {
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.setResolveBindings(true);
|
||||
context.setSourcepath(List.of(sourceRoot.toAbsolutePath().toString()));
|
||||
context.setInlineAccessors(inlineAccessors);
|
||||
context.scan(sourceRoot);
|
||||
return context;
|
||||
}
|
||||
|
||||
private static void writeJava(Path root, String relativePath, String source) throws IOException {
|
||||
Path file = root.resolve(relativePath);
|
||||
Files.createDirectories(file.getParent());
|
||||
Files.writeString(file, source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
digraph statemachine {
|
||||
rankdir=LR;
|
||||
node [shape=rounded, style=filled, fillcolor=white, fontname="Arial"];
|
||||
edge [fontname="Arial", fontsize=10];
|
||||
|
||||
_start [shape=circle, label="", fillcolor=black, width=0.1];
|
||||
_start -> DRAFT;
|
||||
REJECTED [fillcolor=lightgray];
|
||||
APPROVED [fillcolor=lightgray];
|
||||
DRAFT -> SUBMITTED [label="DocumentTransitionEvent.SUBMIT", style="solid", color="black"];
|
||||
SUBMITTED -> APPROVED [label="DocumentTransitionEvent.APPROVE", style="solid", color="black"];
|
||||
SUBMITTED -> REJECTED [label="DocumentTransitionEvent.REJECT", style="solid", color="black"];
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
@startuml
|
||||
!pragma layout smetana
|
||||
set separator none
|
||||
hide empty description
|
||||
hide stereotype
|
||||
skinparam state {
|
||||
BackgroundColor white
|
||||
BorderColor #94a3b8
|
||||
BorderThickness 1
|
||||
FontName Inter
|
||||
FontSize 9
|
||||
FontStyle bold
|
||||
RoundCorner 20
|
||||
Padding 1
|
||||
}
|
||||
skinparam shadowing false
|
||||
skinparam ArrowFontName JetBrains Mono
|
||||
skinparam ArrowFontSize 8
|
||||
skinparam ArrowColor #cbd5e1
|
||||
skinparam ArrowThickness 1
|
||||
skinparam dpi 110
|
||||
skinparam svgLinkTarget _self
|
||||
|
||||
[*] --> DocumentState.DRAFT
|
||||
|
||||
|
||||
DocumentState.DRAFT -[#1E90FF,bold]-> DocumentState.SUBMITTED <<external>> : DocumentTransitionEvent.SUBMIT
|
||||
DocumentState.SUBMITTED -[#1E90FF,bold]-> DocumentState.APPROVED <<external>> : DocumentTransitionEvent.APPROVE
|
||||
DocumentState.SUBMITTED -[#1E90FF,bold]-> DocumentState.REJECTED <<external>> : DocumentTransitionEvent.REJECT
|
||||
|
||||
DocumentState.REJECTED --> [*]
|
||||
DocumentState.APPROVED --> [*]
|
||||
@enduml
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="DRAFT">
|
||||
<state id="DRAFT">
|
||||
<transition target="SUBMITTED" event="DocumentTransitionEvent.SUBMIT"/>
|
||||
</state>
|
||||
<state id="SUBMITTED">
|
||||
<transition target="APPROVED" event="DocumentTransitionEvent.APPROVE"/>
|
||||
<transition target="REJECTED" event="DocumentTransitionEvent.REJECT"/>
|
||||
</state>
|
||||
<state id="APPROVED">
|
||||
</state>
|
||||
<state id="REJECTED">
|
||||
</state>
|
||||
</scxml>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
digraph statemachine {
|
||||
rankdir=LR;
|
||||
node [shape=rounded, style=filled, fillcolor=white, fontname="Arial"];
|
||||
edge [fontname="Arial", fontsize=10];
|
||||
|
||||
_start [shape=circle, label="", fillcolor=black, width=0.1];
|
||||
_start -> NEW;
|
||||
SHIPPED [fillcolor=lightgray];
|
||||
CANCELLED [fillcolor=lightgray];
|
||||
NEW -> PAID [label="OrderTransitionEvent.PAY", style="solid", color="black"];
|
||||
PAID -> SHIPPED [label="OrderTransitionEvent.SHIP", style="solid", color="black"];
|
||||
PAID -> CANCELLED [label="OrderTransitionEvent.CANCEL", style="solid", color="black"];
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
@startuml
|
||||
!pragma layout smetana
|
||||
set separator none
|
||||
hide empty description
|
||||
hide stereotype
|
||||
skinparam state {
|
||||
BackgroundColor white
|
||||
BorderColor #94a3b8
|
||||
BorderThickness 1
|
||||
FontName Inter
|
||||
FontSize 9
|
||||
FontStyle bold
|
||||
RoundCorner 20
|
||||
Padding 1
|
||||
}
|
||||
skinparam shadowing false
|
||||
skinparam ArrowFontName JetBrains Mono
|
||||
skinparam ArrowFontSize 8
|
||||
skinparam ArrowColor #cbd5e1
|
||||
skinparam ArrowThickness 1
|
||||
skinparam dpi 110
|
||||
skinparam svgLinkTarget _self
|
||||
|
||||
[*] --> OrderState.NEW
|
||||
|
||||
|
||||
OrderState.NEW -[#1E90FF,bold]-> OrderState.PAID <<external>> : OrderTransitionEvent.PAY
|
||||
OrderState.PAID -[#1E90FF,bold]-> OrderState.SHIPPED <<external>> : OrderTransitionEvent.SHIP
|
||||
OrderState.PAID -[#1E90FF,bold]-> OrderState.CANCELLED <<external>> : OrderTransitionEvent.CANCEL
|
||||
|
||||
OrderState.SHIPPED --> [*]
|
||||
OrderState.CANCELLED --> [*]
|
||||
@enduml
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="NEW">
|
||||
<state id="NEW">
|
||||
<transition target="PAID" event="OrderTransitionEvent.PAY"/>
|
||||
</state>
|
||||
<state id="PAID">
|
||||
<transition target="SHIPPED" event="OrderTransitionEvent.SHIP"/>
|
||||
<transition target="CANCELLED" event="OrderTransitionEvent.CANCEL"/>
|
||||
</state>
|
||||
<state id="SHIPPED">
|
||||
</state>
|
||||
<state id="CANCELLED">
|
||||
</state>
|
||||
</scxml>
|
||||
|
||||
Reference in New Issue
Block a user