enhanced_state_test.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. package parser
  2. import (
  3. "strings"
  4. "testing"
  5. "mermaid-go/pkg/ast"
  6. "mermaid-go/pkg/renderer"
  7. )
  8. func TestStateParser_EnhancedFeatures(t *testing.T) {
  9. tests := []struct {
  10. name string
  11. input string
  12. expected string
  13. }{
  14. {
  15. name: "Basic State with Actions",
  16. input: `stateDiagram-v2
  17. [*] --> State1
  18. State1 : entry / initialize
  19. State1 : exit / cleanup
  20. State1 : do / process
  21. State1 --> State2 : transition`,
  22. expected: `stateDiagram-v2
  23. [*] --> State1
  24. State1 : entry / initialize
  25. State1 : exit / cleanup
  26. State1 : do / process
  27. State1 --> State2 : transition
  28. `,
  29. },
  30. {
  31. name: "Composite State",
  32. input: `stateDiagram-v2
  33. [*] --> CompositeState
  34. state CompositeState {
  35. [*] --> SubState1
  36. SubState1 --> SubState2
  37. SubState2 --> [*]
  38. }
  39. CompositeState --> [*]`,
  40. expected: `stateDiagram-v2
  41. [*] --> CompositeState
  42. state CompositeState {
  43. [*] --> SubState1
  44. SubState1 --> SubState2
  45. SubState2 --> [*]
  46. }
  47. CompositeState --> [*]
  48. `,
  49. },
  50. {
  51. name: "Parallel States",
  52. input: `stateDiagram-v2
  53. [*] --> ParallelState
  54. state ParallelState {
  55. [*] --> Region1
  56. [*] --> Region2
  57. state Region1 {
  58. [*] --> StateA
  59. StateA --> StateB
  60. }
  61. state Region2 {
  62. [*] --> StateC
  63. StateC --> StateD
  64. }
  65. }`,
  66. expected: `stateDiagram-v2
  67. [*] --> ParallelState
  68. state ParallelState {
  69. [*] --> Region1
  70. [*] --> Region2
  71. state Region1 {
  72. [*] --> StateA
  73. StateA --> StateB
  74. }
  75. state Region2 {
  76. [*] --> StateC
  77. StateC --> StateD
  78. }
  79. }
  80. `,
  81. },
  82. {
  83. name: "Special States",
  84. input: `stateDiagram-v2
  85. [*] --> ForkState
  86. ForkState : <<fork>>
  87. ForkState --> ChoiceState
  88. ChoiceState : <<choice>>
  89. ChoiceState --> JoinState
  90. JoinState : <<join>>
  91. JoinState --> [*]`,
  92. expected: `stateDiagram-v2
  93. state JoinState : <<join>>
  94. state ForkState : <<fork>>
  95. state ChoiceState : <<choice>>
  96. [*] --> ForkState
  97. ForkState --> ChoiceState
  98. ChoiceState --> JoinState
  99. JoinState --> [*]
  100. `,
  101. },
  102. {
  103. name: "Complex Transitions with Guards and Actions",
  104. input: `stateDiagram-v2
  105. [*] --> State1
  106. State1 --> State2 : normal [condition] / action
  107. State2 --> State3 : urgent [priority > 5] / handle
  108. State3 --> [*]`,
  109. expected: `stateDiagram-v2
  110. [*] --> State1
  111. State1 --> State2 : normal [condition] / action
  112. State2 --> State3 : urgent [priority > 5] / handle
  113. State3 --> [*]
  114. `,
  115. },
  116. }
  117. for _, tt := range tests {
  118. t.Run(tt.name, func(t *testing.T) {
  119. parser := NewStateParser()
  120. diagram, err := parser.Parse(tt.input)
  121. if err != nil {
  122. t.Fatalf("Failed to parse: %v", err)
  123. }
  124. // Test rendering
  125. renderer := renderer.NewStateRenderer()
  126. output, err := renderer.Render(diagram)
  127. if err != nil {
  128. t.Fatalf("Failed to render: %v", err)
  129. }
  130. // Normalize whitespace for comparison
  131. expected := strings.TrimSpace(tt.expected)
  132. actual := strings.TrimSpace(output)
  133. if expected != actual {
  134. t.Errorf("Expected:\n%s\n\nGot:\n%s", expected, actual)
  135. }
  136. })
  137. }
  138. }
  139. func TestStateParser_StateActions(t *testing.T) {
  140. input := `stateDiagram-v2
  141. [*] --> State1
  142. State1 : entry / initialize system
  143. State1 : exit / cleanup resources
  144. State1 : do / process data
  145. State1 --> State2`
  146. parser := NewStateParser()
  147. diagram, err := parser.Parse(input)
  148. if err != nil {
  149. t.Fatalf("Failed to parse: %v", err)
  150. }
  151. // Check state was parsed with actions
  152. if len(diagram.States) == 0 {
  153. t.Fatal("No states found")
  154. }
  155. var state1 *ast.StateNode
  156. for _, state := range diagram.States {
  157. if state.ID == "State1" {
  158. state1 = state
  159. break
  160. }
  161. }
  162. if state1 == nil {
  163. t.Fatal("State1 not found")
  164. }
  165. if state1.EntryAction == nil || *state1.EntryAction != "/ initialize system" {
  166. t.Errorf("Expected entry action '/ initialize system', got %v", state1.EntryAction)
  167. }
  168. if state1.ExitAction == nil || *state1.ExitAction != "/ cleanup resources" {
  169. t.Errorf("Expected exit action '/ cleanup resources', got %v", state1.ExitAction)
  170. }
  171. if state1.DoAction == nil || *state1.DoAction != "/ process data" {
  172. t.Errorf("Expected do action '/ process data', got %v", state1.DoAction)
  173. }
  174. }
  175. func TestStateParser_CompositeStates(t *testing.T) {
  176. input := `stateDiagram-v2
  177. [*] --> CompositeState
  178. state CompositeState {
  179. [*] --> SubState1
  180. SubState1 --> SubState2
  181. }
  182. CompositeState --> [*]`
  183. parser := NewStateParser()
  184. diagram, err := parser.Parse(input)
  185. if err != nil {
  186. t.Fatalf("Failed to parse: %v", err)
  187. }
  188. // Check composite state was parsed
  189. var compositeState *ast.StateNode
  190. for _, state := range diagram.States {
  191. if state.ID == "CompositeState" {
  192. compositeState = state
  193. break
  194. }
  195. }
  196. if compositeState == nil {
  197. t.Fatal("CompositeState not found")
  198. }
  199. if compositeState.SubStates == nil || len(compositeState.SubStates) == 0 {
  200. t.Fatal("No substates found in CompositeState")
  201. }
  202. // Check substates
  203. if _, exists := compositeState.SubStates["SubState1"]; !exists {
  204. t.Error("SubState1 not found in CompositeState")
  205. }
  206. if _, exists := compositeState.SubStates["SubState2"]; !exists {
  207. t.Error("SubState2 not found in CompositeState")
  208. }
  209. }