-
-
Notifications
You must be signed in to change notification settings - Fork 564
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Properly consider bases when validating interceptors
Fixes #3664
- Loading branch information
Showing
2 changed files
with
267 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
package expr | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestInterceptorExpr_Validate(t *testing.T) { | ||
cases := map[string]struct { | ||
intercept *InterceptorExpr | ||
method *MethodExpr | ||
wantErrors []string | ||
}{ | ||
"valid-payload": { | ||
intercept: makeInterceptor(t, "test-interceptor", withReadPayload(t, namedAttr(t, "foo"))), | ||
method: makeMethod(t, withPayload(t, namedAttr(t, "foo"))), | ||
}, | ||
"valid-write-payload": { | ||
intercept: makeInterceptor(t, "test-interceptor", withWritePayload(t, namedAttr(t, "foo"))), | ||
method: makeMethod(t, withPayload(t, namedAttr(t, "foo"))), | ||
}, | ||
"payload-with-base": { | ||
intercept: makeInterceptor(t, "test-interceptor", withReadPayload(t, namedAttr(t, "bar"))), | ||
method: makeMethod(t, | ||
withPayload(t, namedAttr(t, "foo")), | ||
withPayloadBases(t, &UserTypeExpr{ | ||
AttributeExpr: &AttributeExpr{ | ||
Type: &Object{namedAttr(t, "bar")}, | ||
}, | ||
}), | ||
), | ||
}, | ||
"result-with-base": { | ||
intercept: makeInterceptor(t, "test-interceptor", withReadResult(t, namedAttr(t, "bar"))), | ||
method: makeMethod(t, | ||
withResult(t, namedAttr(t, "foo")), | ||
withResultBases(t, &UserTypeExpr{ | ||
AttributeExpr: &AttributeExpr{ | ||
Type: &Object{namedAttr(t, "bar")}, | ||
}, | ||
}), | ||
), | ||
}, | ||
"invalid-payload-not-object": { | ||
intercept: makeInterceptor(t, "test-interceptor", withReadPayload(t, namedAttr(t, "foo"))), | ||
method: makeMethod(t, func(m *MethodExpr) { | ||
m.Payload = &AttributeExpr{ | ||
Type: String, | ||
} | ||
}), | ||
wantErrors: []string{ | ||
`interceptor "test-interceptor" cannot be applied because the method payload is not an object`, | ||
}, | ||
}, | ||
"invalid-streaming-payload-not-streaming": { | ||
intercept: makeInterceptor(t, "test-interceptor", withReadStreamingPayload(t, namedAttr(t, "foo"))), | ||
method: makeMethod(t, func(m *MethodExpr) { | ||
m.Payload = &AttributeExpr{Type: &Object{}} | ||
}), | ||
wantErrors: []string{ | ||
`interceptor "test-interceptor" cannot be applied because the method payload is not streaming`, | ||
}, | ||
}, | ||
"invalid-streaming-result-not-streaming": { | ||
intercept: makeInterceptor(t, "test-interceptor", withReadStreamingResult(t, namedAttr(t, "foo"))), | ||
method: makeMethod(t, func(m *MethodExpr) { | ||
m.Result = &AttributeExpr{Type: &Object{}} | ||
}), | ||
wantErrors: []string{ | ||
`interceptor "test-interceptor" cannot be applied because the method result is not streaming`, | ||
}, | ||
}, | ||
"invalid-attribute-access": { | ||
intercept: makeInterceptor(t, "test-interceptor", withReadPayload(t, namedAttr(t, "bar"))), | ||
method: makeMethod(t, withPayload(t, namedAttr(t, "foo"))), | ||
wantErrors: []string{ | ||
`interceptor "test-interceptor" cannot read payload attribute "bar": attribute does not exist`, | ||
}, | ||
}, | ||
} | ||
|
||
for name, tc := range cases { | ||
t.Run(name, func(t *testing.T) { | ||
verr := tc.intercept.validate(tc.method) | ||
if len(tc.wantErrors) != len(verr.Errors) { | ||
t.Errorf("got %d errors, expected %d", len(verr.Errors), len(tc.wantErrors)) | ||
} | ||
for i, err := range verr.Errors { | ||
if i >= len(tc.wantErrors) { | ||
break | ||
} | ||
if got := err.Error(); got != tc.wantErrors[i] { | ||
t.Errorf("got error %q, expected %q", got, tc.wantErrors[i]) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
// Test helpers (at the end of the file) | ||
func withWritePayload(t *testing.T, attrs *NamedAttributeExpr) func(*InterceptorExpr) { | ||
t.Helper() | ||
return func(i *InterceptorExpr) { | ||
i.WritePayload = &AttributeExpr{Type: &Object{attrs}} | ||
} | ||
} | ||
|
||
func withResultBases(t *testing.T, bases ...DataType) func(*MethodExpr) { | ||
t.Helper() | ||
return func(m *MethodExpr) { | ||
if m.Result == nil { | ||
m.Result = &AttributeExpr{Type: &Object{}} | ||
} | ||
m.Result.Bases = bases | ||
} | ||
} | ||
|
||
func makeInterceptor(t *testing.T, name string, opts ...func(*InterceptorExpr)) *InterceptorExpr { | ||
t.Helper() | ||
i := &InterceptorExpr{Name: name} | ||
for _, opt := range opts { | ||
opt(i) | ||
} | ||
return i | ||
} | ||
|
||
// Helper functions need to be updated to handle empty attributes properly | ||
func withReadPayload(t *testing.T, attrs *NamedAttributeExpr) func(*InterceptorExpr) { | ||
t.Helper() | ||
return func(i *InterceptorExpr) { | ||
i.ReadPayload = &AttributeExpr{Type: &Object{attrs}} | ||
} | ||
} | ||
|
||
func withReadResult(t *testing.T, attrs *NamedAttributeExpr) func(*InterceptorExpr) { | ||
t.Helper() | ||
return func(i *InterceptorExpr) { | ||
i.ReadResult = &AttributeExpr{Type: &Object{attrs}} | ||
} | ||
} | ||
|
||
func withReadStreamingPayload(t *testing.T, attrs *NamedAttributeExpr) func(*InterceptorExpr) { | ||
t.Helper() | ||
return func(i *InterceptorExpr) { | ||
i.ReadStreamingPayload = &AttributeExpr{Type: &Object{attrs}} | ||
} | ||
} | ||
|
||
func withReadStreamingResult(t *testing.T, attrs *NamedAttributeExpr) func(*InterceptorExpr) { | ||
t.Helper() | ||
return func(i *InterceptorExpr) { | ||
i.ReadStreamingResult = &AttributeExpr{Type: &Object{attrs}} | ||
} | ||
} | ||
|
||
func makeMethod(t *testing.T, opts ...func(*MethodExpr)) *MethodExpr { | ||
t.Helper() | ||
m := &MethodExpr{} | ||
for _, opt := range opts { | ||
opt(m) | ||
} | ||
return m | ||
} | ||
|
||
func withPayload(t *testing.T, attrs *NamedAttributeExpr) func(*MethodExpr) { | ||
t.Helper() | ||
return func(m *MethodExpr) { | ||
m.Payload = &AttributeExpr{Type: &Object{attrs}} | ||
} | ||
} | ||
|
||
func withPayloadBases(t *testing.T, bases ...DataType) func(*MethodExpr) { | ||
t.Helper() | ||
return func(m *MethodExpr) { | ||
if m.Payload == nil { | ||
m.Payload = &AttributeExpr{Type: &Object{}} | ||
} | ||
m.Payload.Bases = bases | ||
} | ||
} | ||
|
||
func withResult(t *testing.T, attrs *NamedAttributeExpr) func(*MethodExpr) { | ||
t.Helper() | ||
return func(m *MethodExpr) { | ||
m.Result = &AttributeExpr{Type: &Object{attrs}} | ||
} | ||
} | ||
|
||
func namedAttr(t *testing.T, name string) *NamedAttributeExpr { | ||
t.Helper() | ||
return &NamedAttributeExpr{ | ||
Name: name, | ||
Attribute: &AttributeExpr{ | ||
Type: String, | ||
}, | ||
} | ||
} |