Skip to content

Commit c8ef45f

Browse files
refactor: improve unmarshaller struct handling and array element ordering (#17)
1 parent 97a16f9 commit c8ef45f

22 files changed

+1640
-64
lines changed

arazzo/arazzo_order_test.go

Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
package arazzo_test
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"os"
7+
"slices"
8+
"testing"
9+
10+
"github.com/speakeasy-api/openapi/arazzo"
11+
"github.com/speakeasy-api/openapi/arazzo/expression"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
"gopkg.in/yaml.v3"
15+
)
16+
17+
func TestArazzo_ArrayOrdering_ReorderWorkflows_Success(t *testing.T) {
18+
ctx := context.Background()
19+
20+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
21+
require.NoError(t, err)
22+
23+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
24+
require.NoError(t, err)
25+
require.Empty(t, validationErrs)
26+
27+
// Move the first workflow to the end
28+
first := a.Workflows[0]
29+
a.Workflows = slices.Delete(a.Workflows, 0, 1)
30+
a.Workflows = append(a.Workflows, first)
31+
32+
outBuf := bytes.NewBuffer([]byte{})
33+
err = arazzo.Marshal(ctx, a, outBuf)
34+
require.NoError(t, err)
35+
36+
expected, err := os.ReadFile("testdata/ordering/reorder.expected.arazzo.yaml")
37+
require.NoError(t, err)
38+
39+
assert.Equal(t, string(expected), outBuf.String())
40+
}
41+
42+
func TestArazzo_ArrayOrdering_BasicRoundTrip_Success(t *testing.T) {
43+
ctx := context.Background()
44+
45+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
46+
require.NoError(t, err)
47+
48+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
49+
require.NoError(t, err)
50+
require.Empty(t, validationErrs)
51+
52+
// DON'T modify anything - just unmarshal and marshal back
53+
outBuf := bytes.NewBuffer([]byte{})
54+
err = arazzo.Marshal(ctx, a, outBuf)
55+
require.NoError(t, err)
56+
57+
assert.Equal(t, string(data), outBuf.String())
58+
}
59+
60+
func TestArazzo_ArrayOrdering_ReorderWithoutSync_Success(t *testing.T) {
61+
ctx := context.Background()
62+
63+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
64+
require.NoError(t, err)
65+
66+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
67+
require.NoError(t, err)
68+
require.Empty(t, validationErrs)
69+
70+
// Move the first workflow to the end
71+
first := a.Workflows[0]
72+
a.Workflows = slices.Delete(a.Workflows, 0, 1)
73+
a.Workflows = append(a.Workflows, first)
74+
75+
// Marshal WITHOUT calling Sync - directly marshal the core
76+
outBuf := bytes.NewBuffer([]byte{})
77+
err = a.GetCore().Marshal(ctx, outBuf)
78+
require.NoError(t, err)
79+
80+
// When we don't sync, the core should still have the original order
81+
assert.Equal(t, string(data), outBuf.String())
82+
}
83+
84+
func TestArazzo_ArrayOrdering_AddWorkflow_Success(t *testing.T) {
85+
ctx := context.Background()
86+
87+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
88+
require.NoError(t, err)
89+
90+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
91+
require.NoError(t, err)
92+
require.Empty(t, validationErrs)
93+
94+
// Add a new workflow at the end
95+
operationID := expression.Expression("new_operation")
96+
newWorkflow := &arazzo.Workflow{
97+
WorkflowID: "new_workflow",
98+
Steps: []*arazzo.Step{
99+
{
100+
StepID: "new_step",
101+
OperationID: &operationID,
102+
},
103+
},
104+
}
105+
a.Workflows = append(a.Workflows, newWorkflow)
106+
107+
outBuf := bytes.NewBuffer([]byte{})
108+
err = arazzo.Marshal(ctx, a, outBuf)
109+
require.NoError(t, err)
110+
111+
expected, err := os.ReadFile("testdata/ordering/addition.expected.arazzo.yaml")
112+
require.NoError(t, err)
113+
114+
assert.Equal(t, string(expected), outBuf.String())
115+
}
116+
117+
func TestArazzo_ArrayOrdering_DeleteWorkflow_Success(t *testing.T) {
118+
ctx := context.Background()
119+
120+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
121+
require.NoError(t, err)
122+
123+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
124+
require.NoError(t, err)
125+
require.Empty(t, validationErrs)
126+
127+
// Create a new slice with only the workflows we want to keep (remove middle one)
128+
originalWorkflows := a.Workflows
129+
a.Workflows = []*arazzo.Workflow{originalWorkflows[0], originalWorkflows[2]}
130+
131+
outBuf := bytes.NewBuffer([]byte{})
132+
err = arazzo.Marshal(ctx, a, outBuf)
133+
require.NoError(t, err)
134+
135+
expected, err := os.ReadFile("testdata/ordering/deletion.expected.arazzo.yaml")
136+
require.NoError(t, err)
137+
138+
assert.Equal(t, string(expected), outBuf.String())
139+
}
140+
141+
func TestArazzo_ArrayOrdering_ComplexOperations_Success(t *testing.T) {
142+
ctx := context.Background()
143+
144+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
145+
require.NoError(t, err)
146+
147+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
148+
require.NoError(t, err)
149+
require.Empty(t, validationErrs)
150+
151+
// Original order: [workflow-a, workflow-b, workflow-c]
152+
153+
// Step 1: Delete the middle workflow (index 1)
154+
originalWorkflows := a.Workflows
155+
a.Workflows = []*arazzo.Workflow{originalWorkflows[2]}
156+
// Now: [workflow-c]
157+
158+
// Step 2: Add two new workflows
159+
operationID1 := expression.Expression("operation_1")
160+
operationID2 := expression.Expression("operation_2")
161+
inQuery := arazzo.InQuery
162+
contentType := "application/json"
163+
164+
newWorkflow1 := &arazzo.Workflow{
165+
WorkflowID: "new_workflow_1",
166+
Steps: []*arazzo.Step{
167+
{
168+
StepID: "step_1",
169+
OperationID: &operationID1,
170+
Parameters: []*arazzo.ReusableParameter{
171+
{
172+
Object: &arazzo.Parameter{
173+
Name: "param1",
174+
In: &inQuery,
175+
Value: &yaml.Node{Kind: yaml.ScalarNode, Value: "value1"},
176+
},
177+
},
178+
},
179+
},
180+
},
181+
}
182+
183+
newWorkflow2 := &arazzo.Workflow{
184+
WorkflowID: "new_workflow_2",
185+
Steps: []*arazzo.Step{
186+
{
187+
StepID: "step_2",
188+
OperationID: &operationID2,
189+
RequestBody: &arazzo.RequestBody{
190+
ContentType: &contentType,
191+
Payload: &yaml.Node{Kind: yaml.MappingNode, Content: []*yaml.Node{
192+
{Kind: yaml.ScalarNode, Value: "key"},
193+
{Kind: yaml.ScalarNode, Value: "value"},
194+
}},
195+
},
196+
},
197+
},
198+
}
199+
200+
a.Workflows = append(a.Workflows, newWorkflow1, newWorkflow2)
201+
// Now: [workflow-c, new_workflow_1, new_workflow_2]
202+
203+
// Step 3: Reorder - move first to end
204+
first := a.Workflows[0]
205+
a.Workflows = slices.Delete(a.Workflows, 0, 1)
206+
a.Workflows = append(a.Workflows, first)
207+
// Final: [new_workflow_1, new_workflow_2, workflow-c]
208+
209+
outBuf := bytes.NewBuffer([]byte{})
210+
err = arazzo.Marshal(ctx, a, outBuf)
211+
require.NoError(t, err)
212+
213+
expected, err := os.ReadFile("testdata/ordering/complex.expected.arazzo.yaml")
214+
require.NoError(t, err)
215+
216+
assert.Equal(t, string(expected), outBuf.String())
217+
}
218+
219+
func TestArazzo_MapOrdering_StressModification_Success(t *testing.T) {
220+
ctx := context.Background()
221+
222+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
223+
require.NoError(t, err)
224+
225+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
226+
require.NoError(t, err)
227+
require.Empty(t, validationErrs)
228+
229+
// Stress test: Multiple operations that should trigger sync
230+
231+
// 1. Reorder parameters multiple times
232+
for i := 0; i < 3; i++ {
233+
paramA, _ := a.Components.Parameters.Get("param-a")
234+
paramB, _ := a.Components.Parameters.Get("param-b")
235+
paramC, _ := a.Components.Parameters.Get("param-c")
236+
237+
a.Components.Parameters.Delete("param-a")
238+
a.Components.Parameters.Delete("param-b")
239+
a.Components.Parameters.Delete("param-c")
240+
241+
// Different order each time
242+
switch i {
243+
case 0:
244+
a.Components.Parameters.Set("param-c", paramC)
245+
a.Components.Parameters.Set("param-a", paramA)
246+
a.Components.Parameters.Set("param-b", paramB)
247+
case 1:
248+
a.Components.Parameters.Set("param-b", paramB)
249+
a.Components.Parameters.Set("param-c", paramC)
250+
a.Components.Parameters.Set("param-a", paramA)
251+
case 2:
252+
a.Components.Parameters.Set("param-a", paramA)
253+
a.Components.Parameters.Set("param-c", paramC)
254+
a.Components.Parameters.Set("param-b", paramB)
255+
}
256+
}
257+
258+
// 2. Modify parameter fields to force sync
259+
paramA, _ := a.Components.Parameters.Get("param-a")
260+
paramA.Name = "modified-param-a"
261+
paramA.Value = &yaml.Node{Kind: yaml.ScalarNode, Value: "modified-value"}
262+
a.Components.Parameters.Set("param-a", paramA)
263+
264+
// 3. Add and remove a temporary parameter
265+
inHeader := arazzo.InHeader
266+
tempParam := &arazzo.Parameter{
267+
Name: "temp-param",
268+
In: &inHeader,
269+
Value: &yaml.Node{Kind: yaml.ScalarNode, Value: "temp-value"},
270+
}
271+
a.Components.Parameters.Set("temp-param", tempParam)
272+
a.Components.Parameters.Delete("temp-param")
273+
274+
outBuf := bytes.NewBuffer([]byte{})
275+
err = arazzo.Marshal(ctx, a, outBuf)
276+
require.NoError(t, err)
277+
278+
expected, err := os.ReadFile("testdata/ordering/map-stress.expected.arazzo.yaml")
279+
require.NoError(t, err)
280+
281+
assert.Equal(t, string(expected), outBuf.String())
282+
}
283+
284+
func TestArazzo_MapOrdering_AddParameter_Success(t *testing.T) {
285+
ctx := context.Background()
286+
287+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
288+
require.NoError(t, err)
289+
290+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
291+
require.NoError(t, err)
292+
require.Empty(t, validationErrs)
293+
294+
// Add a new parameter to the components
295+
inHeader := arazzo.InHeader
296+
newParam := &arazzo.Parameter{
297+
Name: "param-new",
298+
In: &inHeader,
299+
Value: &yaml.Node{Kind: yaml.ScalarNode, Value: "new-value"},
300+
}
301+
a.Components.Parameters.Set("param-new", newParam)
302+
303+
outBuf := bytes.NewBuffer([]byte{})
304+
err = arazzo.Marshal(ctx, a, outBuf)
305+
require.NoError(t, err)
306+
307+
expected, err := os.ReadFile("testdata/ordering/map-addition.expected.arazzo.yaml")
308+
require.NoError(t, err)
309+
310+
assert.Equal(t, string(expected), outBuf.String())
311+
}
312+
313+
func TestArazzo_MapOrdering_DeleteParameter_Success(t *testing.T) {
314+
ctx := context.Background()
315+
316+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
317+
require.NoError(t, err)
318+
319+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
320+
require.NoError(t, err)
321+
require.Empty(t, validationErrs)
322+
323+
// Delete parameters and success/failure actions (delete the middle ones)
324+
a.Components.Parameters.Delete("param-b")
325+
a.Components.SuccessActions.Delete("success-b")
326+
a.Components.FailureActions.Delete("failure-b")
327+
328+
outBuf := bytes.NewBuffer([]byte{})
329+
err = arazzo.Marshal(ctx, a, outBuf)
330+
require.NoError(t, err)
331+
332+
expected, err := os.ReadFile("testdata/ordering/map-deletion.expected.arazzo.yaml")
333+
require.NoError(t, err)
334+
335+
assert.Equal(t, string(expected), outBuf.String())
336+
}
337+
338+
func TestArazzo_MapOrdering_ReorderComponents_Success(t *testing.T) {
339+
ctx := context.Background()
340+
341+
data, err := os.ReadFile("testdata/ordering/input.arazzo.yaml")
342+
require.NoError(t, err)
343+
344+
a, validationErrs, err := arazzo.Unmarshal(ctx, bytes.NewBuffer(data), arazzo.WithSkipValidation())
345+
require.NoError(t, err)
346+
require.Empty(t, validationErrs)
347+
348+
// Get all parameters
349+
paramA, hasA := a.Components.Parameters.Get("param-a")
350+
paramB, hasB := a.Components.Parameters.Get("param-b")
351+
paramC, hasC := a.Components.Parameters.Get("param-c")
352+
353+
require.True(t, hasA, "param-a should exist")
354+
require.True(t, hasB, "param-b should exist")
355+
require.True(t, hasC, "param-c should exist")
356+
357+
// Clear and re-add in different order: c, a, b
358+
a.Components.Parameters.Delete("param-a")
359+
a.Components.Parameters.Delete("param-b")
360+
a.Components.Parameters.Delete("param-c")
361+
362+
a.Components.Parameters.Set("param-c", paramC)
363+
a.Components.Parameters.Set("param-a", paramA)
364+
a.Components.Parameters.Set("param-b", paramB)
365+
366+
outBuf := bytes.NewBuffer([]byte{})
367+
err = arazzo.Marshal(ctx, a, outBuf)
368+
require.NoError(t, err)
369+
370+
expected, err := os.ReadFile("testdata/ordering/map-reorder.expected.arazzo.yaml")
371+
require.NoError(t, err)
372+
373+
assert.Equal(t, string(expected), outBuf.String())
374+
}

0 commit comments

Comments
 (0)