Files
tasks/chasm/lib/callback/validator_test.go
Fred TzengandGitHub 7b4d18dd4f Add context.Context to callback.Validator interface (#10140)
## What changed?
Added `context.Context` as the first parameter to the
`callback.Validator` interface and threaded it through all call
sites (`WorkflowHandler`, `activity.FrontendHandler`, and their internal
validation/preparation methods). The base validator ignores the context.
No behavioral change.

## Why?
Deployments that decorate the validator (via `fx.Decorate`) need access
to the gRPC context to check caller identity — e.g. to allow internal
callers (scheduler) to attach `temporal://internal` callbacks while
rejecting them from external users.

## How did you test it?
- [X] built
- [X] run locally and tested manually
- [X] covered by existing tests
- [ ] added new unit test(s)
- [ ] added new functional test(s)
2026-05-12 08:28:35 -07:00

154 lines
4.7 KiB
Go

package callback
import (
"context"
"regexp"
"testing"
"github.com/stretchr/testify/require"
commonpb "go.temporal.io/api/common/v1"
"go.temporal.io/api/serviceerror"
)
func TestValidateCallbacks(t *testing.T) {
allowAll := AddressMatchRules{
Rules: []AddressMatchRule{
{Regexp: regexp.MustCompile(`.*`), AllowInsecure: true},
},
}
v := NewValidator(
func(string) int { return 10 },
func(string) int { return 1000 },
func(string) int { return 4096 },
func(string) AddressMatchRules { return allowAll },
)
t.Run("ValidNexusCallback", func(t *testing.T) {
cbs := []*commonpb.Callback{
{Variant: &commonpb.Callback_Nexus_{
Nexus: &commonpb.Callback_Nexus{
Url: "http://localhost:8080/callback",
Header: map[string]string{"Content-Type": "application/json"},
},
}},
}
err := v.Validate(context.Background(), "ns", cbs)
require.NoError(t, err)
})
t.Run("TooManyCallbacks", func(t *testing.T) {
v := NewValidator(
func(string) int { return 1 },
func(string) int { return 1000 },
func(string) int { return 4096 },
func(string) AddressMatchRules { return allowAll },
)
cbs := []*commonpb.Callback{
{Variant: &commonpb.Callback_Nexus_{Nexus: &commonpb.Callback_Nexus{Url: "http://localhost/cb1"}}},
{Variant: &commonpb.Callback_Nexus_{Nexus: &commonpb.Callback_Nexus{Url: "http://localhost/cb2"}}},
}
err := v.Validate(context.Background(), "ns", cbs)
var invalidArgErr *serviceerror.InvalidArgument
require.ErrorAs(t, err, &invalidArgErr)
require.Contains(t, err.Error(), "cannot attach more than 1 callbacks")
})
t.Run("URLTooLong", func(t *testing.T) {
v := NewValidator(
func(string) int { return 10 },
func(string) int { return 50 },
func(string) int { return 4096 },
func(string) AddressMatchRules { return allowAll },
)
cbs := []*commonpb.Callback{
{Variant: &commonpb.Callback_Nexus_{
Nexus: &commonpb.Callback_Nexus{
Url: "http://localhost/" + string(make([]byte, 51)),
},
}},
}
err := v.Validate(context.Background(), "ns", cbs)
var invalidArgErr *serviceerror.InvalidArgument
require.ErrorAs(t, err, &invalidArgErr)
require.Contains(t, err.Error(), "url length longer than max length allowed")
})
t.Run("HeaderTooLarge", func(t *testing.T) {
cbs := []*commonpb.Callback{
{Variant: &commonpb.Callback_Nexus_{
Nexus: &commonpb.Callback_Nexus{
Url: "http://localhost:8080/callback",
Header: map[string]string{"X-Large": string(make([]byte, 5000))},
},
}},
}
err := v.Validate(context.Background(), "ns", cbs)
var invalidArgErr *serviceerror.InvalidArgument
require.ErrorAs(t, err, &invalidArgErr)
require.Contains(t, err.Error(), "header size longer than max allowed size")
})
t.Run("HeaderKeysNormalizedToLowercase", func(t *testing.T) {
cbs := []*commonpb.Callback{
{Variant: &commonpb.Callback_Nexus_{
Nexus: &commonpb.Callback_Nexus{
Url: "http://localhost:8080/callback",
Header: map[string]string{"Content-Type": "application/json", "X-Custom": "value"},
},
}},
}
err := v.Validate(context.Background(), "ns", cbs)
require.NoError(t, err)
nexus := cbs[0].GetNexus()
require.Equal(t, "application/json", nexus.Header["content-type"])
require.Equal(t, "value", nexus.Header["x-custom"])
_, hasMixed := nexus.Header["Content-Type"]
require.False(t, hasMixed)
})
t.Run("URLNotInAllowlist", func(t *testing.T) {
v := NewValidator(
func(string) int { return 10 },
func(string) int { return 1000 },
func(string) int { return 4096 },
func(string) AddressMatchRules { return AddressMatchRules{} },
)
cbs := []*commonpb.Callback{
{Variant: &commonpb.Callback_Nexus_{
Nexus: &commonpb.Callback_Nexus{
Url: "http://localhost:8080/callback",
},
}},
}
err := v.Validate(context.Background(), "ns", cbs)
var invalidArgErr *serviceerror.InvalidArgument
require.ErrorAs(t, err, &invalidArgErr)
require.Contains(t, err.Error(), "does not match any configured callback address")
})
t.Run("UnsupportedVariant", func(t *testing.T) {
cbs := []*commonpb.Callback{
{Variant: nil},
}
err := v.Validate(context.Background(), "ns", cbs)
var unimplementedErr *serviceerror.Unimplemented
require.ErrorAs(t, err, &unimplementedErr)
require.Contains(t, err.Error(), "unknown callback variant")
})
t.Run("EmptyCallbacksNoError", func(t *testing.T) {
err := v.Validate(context.Background(), "ns", nil)
require.NoError(t, err)
})
t.Run("InternalCallbackSkipped", func(t *testing.T) {
cbs := []*commonpb.Callback{
{Variant: &commonpb.Callback_Internal_{
Internal: &commonpb.Callback_Internal{},
}},
}
err := v.Validate(context.Background(), "ns", cbs)
require.NoError(t, err)
})
}