Files
tasks/chasm/registry_test.go
Yichao YangandGitHub 89a4eb7855 CHASM: Engine Update/ReadComponent implementation (#7696)
## What changed?
- Implement chasm engine Update/Read Component method

## Why?
- CHASM work stream

## How did you test it?
- [x] built
- [ ] run locally and tested manually
- [ ] covered by existing tests
- [x] added new unit test(s)
- [ ] added new functional test(s)
2025-05-08 19:21:27 -07:00

317 lines
10 KiB
Go

package chasm_test
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
"go.temporal.io/server/chasm"
"go.uber.org/mock/gomock"
)
type (
testTask1 struct{}
testTask2 struct{}
testTaskComponentInterface interface {
DoSomething()
}
)
func TestRegistry_RegisterComponents_Success(t *testing.T) {
r := chasm.NewRegistry()
ctrl := gomock.NewController(t)
lib := chasm.NewMockLibrary(ctrl)
lib.EXPECT().Name().Return("TestLibrary").AnyTimes()
lib.EXPECT().Components().Return([]*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*chasm.MockComponent]("Component1"),
})
lib.EXPECT().Tasks().Return(nil)
err := r.Register(lib)
require.NoError(t, err)
rc1, ok := r.Component("TestLibrary.Component1")
require.True(t, ok)
require.Equal(t, "TestLibrary.Component1", rc1.FqType())
missingRC, ok := r.Component("TestLibrary.Component2")
require.False(t, ok)
require.Nil(t, missingRC)
cInstance1 := chasm.NewMockComponent(ctrl)
rc2, ok := r.ComponentFor(cInstance1)
require.True(t, ok)
require.Equal(t, "TestLibrary.Component1", rc2.FqType())
rc2, ok = r.ComponentOf(reflect.TypeOf(cInstance1))
require.True(t, ok)
require.Equal(t, "TestLibrary.Component1", rc2.FqType())
cInstance2 := "invalid component instance"
rc3, ok := r.ComponentFor(cInstance2)
require.False(t, ok)
require.Nil(t, rc3)
}
func TestRegistry_RegisterTasks_Success(t *testing.T) {
r := chasm.NewRegistry()
ctrl := gomock.NewController(t)
lib := chasm.NewMockLibrary(ctrl)
lib.EXPECT().Name().Return("TestLibrary").AnyTimes()
lib.EXPECT().Components().Return(nil)
lib.EXPECT().Tasks().Return([]*chasm.RegistrableTask{
chasm.NewRegistrableSideEffectTask[*chasm.MockComponent, testTask1](
"Task1",
chasm.NewMockTaskValidator[*chasm.MockComponent, testTask1](ctrl),
chasm.NewMockSideEffectTaskExecutor[*chasm.MockComponent, testTask1](ctrl),
),
chasm.NewRegistrablePureTask[testTaskComponentInterface, testTask2](
"Task2",
chasm.NewMockTaskValidator[testTaskComponentInterface, testTask2](ctrl),
chasm.NewMockPureTaskExecutor[testTaskComponentInterface, testTask2](ctrl),
),
})
err := r.Register(lib)
require.NoError(t, err)
rt1, ok := r.Task("TestLibrary.Task1")
require.True(t, ok)
require.Equal(t, "TestLibrary.Task1", rt1.FqType())
missingRT, ok := r.Task("TestLibrary.TaskMissing")
require.False(t, ok)
require.Nil(t, missingRT)
tInstance1 := testTask2{}
rt2, ok := r.TaskFor(tInstance1)
require.True(t, ok)
require.Equal(t, "TestLibrary.Task2", rt2.FqType())
rt2, ok = r.TaskOf(reflect.TypeOf(tInstance1))
require.True(t, ok)
require.Equal(t, "TestLibrary.Task2", rt2.FqType())
tInstance2 := "invalid task instance"
rt3, ok := r.TaskFor(tInstance2)
require.False(t, ok)
require.Nil(t, rt3)
}
func TestRegistry_Register_LibraryError(t *testing.T) {
ctrl := gomock.NewController(t)
lib := chasm.NewMockLibrary(ctrl)
t.Run("library name must not be empty", func(t *testing.T) {
lib.EXPECT().Name().Return("")
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "name must not be empty")
})
t.Run("library name must follow rules", func(t *testing.T) {
lib.EXPECT().Name().Return("bad.lib.name")
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "name must follow golang identifier rules")
})
}
func TestRegistry_RegisterComponents_Error(t *testing.T) {
ctrl := gomock.NewController(t)
lib := chasm.NewMockLibrary(ctrl)
lib.EXPECT().Name().Return("TestLibrary").AnyTimes()
t.Run("component name must not be empty", func(t *testing.T) {
lib.EXPECT().Components().Return([]*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*chasm.MockComponent](""),
})
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "name must not be empty")
})
t.Run("component name must follow rules", func(t *testing.T) {
lib.EXPECT().Components().Return([]*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*chasm.MockComponent]("bad.component.name"),
})
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "name must follow golang identifier rules")
})
t.Run("component is already registered by name", func(t *testing.T) {
lib.EXPECT().Components().Return([]*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*chasm.MockComponent]("Component1"),
chasm.NewRegistrableComponent[*chasm.MockComponent]("Component1"),
})
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "is already registered")
})
t.Run("component is already registered by type", func(t *testing.T) {
lib.EXPECT().Components().Return([]*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[*chasm.MockComponent]("Component1"),
chasm.NewRegistrableComponent[*chasm.MockComponent]("Component2"),
})
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "is already registered")
})
t.Run("component is already registered in another library", func(t *testing.T) {
lib2 := chasm.NewMockLibrary(ctrl)
lib2.EXPECT().Name().Return("TestLibrary2").AnyTimes()
component := chasm.NewRegistrableComponent[*chasm.MockComponent]("Component1")
lib2.EXPECT().Components().Return([]*chasm.RegistrableComponent{
component,
})
lib2.EXPECT().Tasks().Return(nil)
r2 := chasm.NewRegistry()
err := r2.Register(lib2)
require.NoError(t, err)
lib.EXPECT().Components().Return([]*chasm.RegistrableComponent{
component,
})
r := chasm.NewRegistry()
err = r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "is already registered in library TestLibrary2")
})
t.Run("component must be a struct", func(t *testing.T) {
lib.EXPECT().Components().Return([]*chasm.RegistrableComponent{
chasm.NewRegistrableComponent[chasm.Component]("Component1"),
})
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "must be struct or pointer to struct")
})
}
func TestRegistry_RegisterTasks_Error(t *testing.T) {
ctrl := gomock.NewController(t)
lib := chasm.NewMockLibrary(ctrl)
lib.EXPECT().Name().Return("TestLibrary").AnyTimes()
lib.EXPECT().Components().Return(nil).AnyTimes()
t.Run("task name must not be empty", func(t *testing.T) {
r := chasm.NewRegistry()
lib.EXPECT().Tasks().Return([]*chasm.RegistrableTask{
chasm.NewRegistrablePureTask[*chasm.MockComponent, testTask1](
"",
chasm.NewMockTaskValidator[*chasm.MockComponent, testTask1](ctrl),
chasm.NewMockPureTaskExecutor[*chasm.MockComponent, testTask1](ctrl),
),
})
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "name must not be empty")
})
t.Run("task name must follow rules", func(t *testing.T) {
lib.EXPECT().Tasks().Return([]*chasm.RegistrableTask{
chasm.NewRegistrablePureTask[*chasm.MockComponent, testTask1](
"bad.task.name",
chasm.NewMockTaskValidator[*chasm.MockComponent, testTask1](ctrl),
chasm.NewMockPureTaskExecutor[*chasm.MockComponent, testTask1](ctrl),
),
})
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "name must follow golang identifier rules")
})
t.Run("task is already registered by name", func(t *testing.T) {
lib.EXPECT().Tasks().Return([]*chasm.RegistrableTask{
chasm.NewRegistrablePureTask[*chasm.MockComponent, testTask1](
"Task1",
chasm.NewMockTaskValidator[*chasm.MockComponent, testTask1](ctrl),
chasm.NewMockPureTaskExecutor[*chasm.MockComponent, testTask1](ctrl),
),
chasm.NewRegistrableSideEffectTask[*chasm.MockComponent, testTask1](
"Task1",
chasm.NewMockTaskValidator[*chasm.MockComponent, testTask1](ctrl),
chasm.NewMockSideEffectTaskExecutor[*chasm.MockComponent, testTask1](ctrl),
),
})
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "is already registered")
})
t.Run("task is already registered by type", func(t *testing.T) {
lib.EXPECT().Tasks().Return([]*chasm.RegistrableTask{
chasm.NewRegistrablePureTask[*chasm.MockComponent, testTask1](
"Task1",
chasm.NewMockTaskValidator[*chasm.MockComponent, testTask1](ctrl),
chasm.NewMockPureTaskExecutor[*chasm.MockComponent, testTask1](ctrl),
),
chasm.NewRegistrablePureTask[*chasm.MockComponent, testTask1](
"Task2",
chasm.NewMockTaskValidator[*chasm.MockComponent, testTask1](ctrl),
chasm.NewMockPureTaskExecutor[*chasm.MockComponent, testTask1](ctrl),
),
})
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "is already registered")
})
t.Run("component is already registered in another library", func(t *testing.T) {
lib2 := chasm.NewMockLibrary(ctrl)
lib2.EXPECT().Name().Return("TestLibrary2").AnyTimes()
lib2.EXPECT().Components().Return(nil)
task := chasm.NewRegistrablePureTask[*chasm.MockComponent, testTask1](
"Task1",
chasm.NewMockTaskValidator[*chasm.MockComponent, testTask1](ctrl),
chasm.NewMockPureTaskExecutor[*chasm.MockComponent, testTask1](ctrl),
)
lib2.EXPECT().Tasks().Return([]*chasm.RegistrableTask{task})
r2 := chasm.NewRegistry()
err := r2.Register(lib2)
require.NoError(t, err)
lib.EXPECT().Tasks().Return([]*chasm.RegistrableTask{task})
r := chasm.NewRegistry()
err = r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "is already registered in library TestLibrary2")
})
t.Run("task must be struct", func(t *testing.T) {
lib.EXPECT().Tasks().Return([]*chasm.RegistrableTask{
chasm.NewRegistrablePureTask[*chasm.MockComponent, string](
"Task1",
chasm.NewMockTaskValidator[*chasm.MockComponent, string](ctrl),
chasm.NewMockPureTaskExecutor[*chasm.MockComponent, string](ctrl),
),
})
r := chasm.NewRegistry()
err := r.Register(lib)
require.Error(t, err)
require.Contains(t, err.Error(), "must be struct or pointer to struct")
})
}