Skip to content

Commit c7a2e55

Browse files
committed
fix: refactor ValidateBricks to return a single error and update related tests for consistency
1 parent 6a17436 commit c7a2e55

File tree

3 files changed

+43
-58
lines changed

3 files changed

+43
-58
lines changed

internal/orchestrator/app/parser.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,35 +142,35 @@ func (a *AppDescriptor) IsValid() error {
142142

143143
// ValidateBricks checks that all bricks referenced in the given AppDescriptor exist in the provided BricksIndex,
144144
// and that all required variables for each brick are present and valid. It collects and returns all validation
145-
// errors found as a slice, allowing the caller to see all issues at once rather than stopping at the first error.
145+
// errors as a single joined error, allowing the caller to see all issues at once rather than stopping at the first error.
146146
// If the index is nil, validation is skipped and nil is returned.
147-
func (a *AppDescriptor) ValidateBricks(index *bricksindex.BricksIndex) []error {
147+
func (a *AppDescriptor) ValidateBricks(index *bricksindex.BricksIndex) error {
148148
if index == nil {
149149
return nil
150150
}
151151

152-
var allErrors []error
152+
var allErrors error
153153

154154
for _, appBrick := range a.Bricks {
155155
indexBrick, found := index.FindBrickByID(appBrick.ID)
156156
if !found {
157-
allErrors = append(allErrors, fmt.Errorf("brick %q not found", appBrick.ID))
157+
allErrors = errors.Join(allErrors, fmt.Errorf("brick %q not found", appBrick.ID))
158158
continue // Skip further validation for this brick since it doesn't exist
159159
}
160160

161161
// Check that all app variables exist in brick definition
162162
for appBrickName := range appBrick.Variables {
163163
_, exist := indexBrick.GetVariable(appBrickName)
164164
if !exist {
165-
allErrors = append(allErrors, fmt.Errorf("variable %q does not exist on brick %q", appBrickName, indexBrick.ID))
165+
allErrors = errors.Join(allErrors, fmt.Errorf("variable %q does not exist on brick %q", appBrickName, indexBrick.ID))
166166
}
167167
}
168168

169169
// Check that all required brick variables are provided by app
170170
for _, indexBrickVariable := range indexBrick.Variables {
171171
if indexBrickVariable.IsRequired() {
172172
if _, exist := appBrick.Variables[indexBrickVariable.Name]; !exist {
173-
allErrors = append(allErrors, fmt.Errorf("variable %q is required by brick %q", indexBrickVariable.Name, indexBrick.ID))
173+
allErrors = errors.Join(allErrors, fmt.Errorf("variable %q is required by brick %q", indexBrickVariable.Name, indexBrick.ID))
174174
}
175175
}
176176
}

internal/orchestrator/app/validator_test.go

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,58 +18,52 @@ func TestValidateAppDescriptorBricks(t *testing.T) {
1818
require.NotNil(t, bricksIndex)
1919

2020
testCases := []struct {
21-
name string
22-
filename string
23-
expectedErrors []error // Now expecting a slice of error messages
21+
name string
22+
filename string
23+
expectedError error
2424
}{
2525
{
26-
name: "valid with all required filled",
27-
filename: "all-required-app.yaml",
28-
expectedErrors: nil,
26+
name: "valid with all required filled",
27+
filename: "all-required-app.yaml",
28+
expectedError: nil,
2929
},
3030
{
31-
name: "valid with missing bricks",
32-
filename: "no-bricks-app.yaml",
33-
expectedErrors: nil,
31+
name: "valid with missing bricks",
32+
filename: "no-bricks-app.yaml",
33+
expectedError: nil,
3434
},
3535
{
36-
name: "valid with empty list of bricks",
37-
filename: "empty-bricks-app.yaml",
38-
expectedErrors: nil,
36+
name: "valid with empty list of bricks",
37+
filename: "empty-bricks-app.yaml",
38+
expectedError: nil,
3939
},
4040
{
41-
name: "valid if required variable is empty string",
42-
filename: "empty-required-app.yaml",
43-
expectedErrors: nil,
41+
name: "valid if required variable is empty string",
42+
filename: "empty-required-app.yaml",
43+
expectedError: nil,
4444
},
4545
{
4646
name: "invalid if required variable is omitted",
4747
filename: "omitted-required-app.yaml",
48-
expectedErrors: []error{
48+
expectedError: errors.Join(
4949
errors.New("variable \"ARDUINO_DEVICE_ID\" is required by brick \"arduino:arduino_cloud\""),
5050
errors.New("variable \"ARDUINO_SECRET\" is required by brick \"arduino:arduino_cloud\""),
51-
},
51+
),
5252
},
5353
{
54-
name: "invalid if a required variable among two is omitted",
55-
filename: "omitted-mixed-required-app.yaml",
56-
expectedErrors: []error{
57-
errors.New("variable \"ARDUINO_SECRET\" is required by brick \"arduino:arduino_cloud\""),
58-
},
54+
name: "invalid if a required variable among two is omitted",
55+
filename: "omitted-mixed-required-app.yaml",
56+
expectedError: errors.New("variable \"ARDUINO_SECRET\" is required by brick \"arduino:arduino_cloud\""),
5957
},
6058
{
61-
name: "invalid if brick id not found",
62-
filename: "not-found-brick-app.yaml",
63-
expectedErrors: []error{
64-
errors.New("brick \"arduino:not_existing_brick\" not found"),
65-
},
59+
name: "invalid if brick id not found",
60+
filename: "not-found-brick-app.yaml",
61+
expectedError: errors.New("brick \"arduino:not_existing_brick\" not found"),
6662
},
6763
{
68-
name: "invalid if variable does not exist in the brick",
69-
filename: "not-found-variable-app.yaml",
70-
expectedErrors: []error{
71-
errors.New("variable \"NOT_EXISTING_VARIABLE\" does not exist on brick \"arduino:arduino_cloud\""),
72-
},
64+
name: "invalid if variable does not exist in the brick",
65+
filename: "not-found-variable-app.yaml",
66+
expectedError: errors.New("variable \"NOT_EXISTING_VARIABLE\" does not exist on brick \"arduino:arduino_cloud\""),
7367
},
7468
}
7569

@@ -78,13 +72,12 @@ func TestValidateAppDescriptorBricks(t *testing.T) {
7872
appDescriptor, err := ParseDescriptorFile(paths.New("testdata/validator/" + tc.filename))
7973
require.NoError(t, err)
8074

81-
errs := appDescriptor.ValidateBricks(bricksIndex)
82-
if tc.expectedErrors == nil {
83-
require.Nil(t, errs)
84-
assert.Empty(t, errs, "Expected no validation errors")
75+
err = appDescriptor.ValidateBricks(bricksIndex)
76+
if tc.expectedError == nil {
77+
assert.NoError(t, err, "Expected no validation errors")
8578
} else {
86-
require.Len(t, errs, len(tc.expectedErrors), "Expected %d errors but got %d", len(tc.expectedErrors), len(errs))
87-
require.Exactly(t, tc.expectedErrors, errs)
79+
require.Error(t, err, "Expected validation error")
80+
assert.Equal(t, tc.expectedError.Error(), err.Error(), "Error message should match")
8881
}
8982
})
9083
}

internal/orchestrator/orchestrator.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,9 @@ func StartApp(
122122
ctx, cancel := context.WithCancel(ctx)
123123
defer cancel()
124124

125-
errs := app.Descriptor.ValidateBricks(bricksIndex)
126-
if errs != nil {
127-
for _, e := range errs {
128-
if !yield(StreamMessage{error: e}) {
129-
return
130-
}
131-
}
125+
err := app.Descriptor.ValidateBricks(bricksIndex)
126+
if err != nil {
127+
yield(StreamMessage{error: err})
132128
return
133129
}
134130

@@ -457,13 +453,9 @@ func RestartApp(
457453
ctx, cancel := context.WithCancel(ctx)
458454
defer cancel()
459455

460-
errs := appToStart.Descriptor.ValidateBricks(bricksIndex)
461-
if errs != nil {
462-
for _, e := range errs {
463-
if !yield(StreamMessage{error: e}) {
464-
return
465-
}
466-
}
456+
err := appToStart.Descriptor.ValidateBricks(bricksIndex)
457+
if err != nil {
458+
yield(StreamMessage{error: err})
467459
return
468460
}
469461

0 commit comments

Comments
 (0)