Files
tasks/components/callbacks/request_test.go
zeekay 40351cce6f rip: delete temporal fork — native ZAP-only tasksd
Goes from 135 MB binary with 1153 files importing go.temporal.io and 224
importing google.golang.org/grpc to a 10 MB native binary with ZERO of
either.

Deleted:
- tasks/, tasktests/, service/, chasm/, client/, api/, proto/, tools/
- cmd/tools/, components/, docker/
- schema/{cassandra,elasticsearch,mysql,postgresql,sqlite}/
- 40 tainted common/ subdirs + 7 top-level common files
- tests/testutils (no longer referenced after surviving deps drop common)

New cmd/tasksd (~100 lines) boots tasks.Embed (zap.Node) + http.Server.
New pkg/tasks/embed.go registers ZAP handlers for the SDK opcode surface
(0x0060-0x00A5). Workflow handlers return a 501 "not yet implemented"
envelope; native engine lands behind this contract.

Build:  go build ./cmd/tasksd → 10 MB
Vet:    go vet ./... → clean
Boot:   tasksd --zap-port 9999 --http :7243
Probes: /healthz, /v1/tasks/health → 200
UI:     /_/tasks/, / → 200 (embedded Vite bundle)
ZAP:    _tasks._tcp on :9999 (mDNS disabled; direct connect)

go.mod: tidy. No go.temporal.io. No google.golang.org/grpc.
2026-04-26 14:06:21 -07:00

329 lines
9.5 KiB
Go

package callbacks
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/nexus-rpc/sdk-go/nexus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.temporal.io/api/serviceerror"
persistencespb "go.temporal.io/server/api/persistence/v1"
tokenspb "go.temporal.io/server/api/token/v1"
"go.temporal.io/server/common"
"go.temporal.io/server/common/cluster"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/namespace"
commonnexus "go.temporal.io/server/common/nexus"
"go.uber.org/mock/gomock"
)
func newTestFrontendHTTPClient(ts *httptest.Server) *common.FrontendHTTPClient {
u, _ := url.Parse(ts.URL)
return &common.FrontendHTTPClient{
Client: *ts.Client(),
Address: u.Host,
Scheme: u.Scheme,
}
}
func TestRouteRequest_ExternalTarget(t *testing.T) {
// When no source header is set, the request should be sent via the default client.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
ctrl := gomock.NewController(t)
clusterMeta := cluster.NewMockMetadata(ctrl)
r, err := http.NewRequest(http.MethodPost, ts.URL+"/some/path", nil)
require.NoError(t, err)
resp, err := routeRequest(
r,
clusterMeta,
nil, // namespaceRegistry not needed for external targets
nil, // httpClientCache not needed for external targets
nil, // callbackTokenGenerator not needed for external targets
ts.Client(),
nil, // localClient not needed for external targets
log.NewNoopLogger(),
)
require.NoError(t, err)
defer func() { _ = resp.Body.Close() }()
require.Equal(t, http.StatusOK, resp.StatusCode)
}
func TestRouteRequest_SourceHeaderLocal(t *testing.T) {
// When the source header matches the local cluster, the request should be routed to the local client.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted)
}))
defer ts.Close()
ctrl := gomock.NewController(t)
clusterMeta := cluster.NewMockMetadata(ctrl)
clusterMeta.EXPECT().GetAllClusterInfo().Return(map[string]cluster.ClusterInformation{
"cluster-A": {ClusterID: "cluster-id-A"},
})
clusterMeta.EXPECT().GetCurrentClusterName().Return("cluster-A")
localClient := newTestFrontendHTTPClient(ts)
r, err := http.NewRequest(http.MethodPost, "http://original-host/some/path", nil)
require.NoError(t, err)
r.Header.Set(callbackSourceHeader, "cluster-id-A")
resp, err := routeRequest(
r,
clusterMeta,
nil, // namespaceRegistry
nil, // httpClientCache - not used since it's the local cluster
nil, // callbackTokenGenerator
&http.Client{},
localClient,
log.NewNoopLogger(),
)
require.NoError(t, err)
defer func() { _ = resp.Body.Close() }()
require.Equal(t, http.StatusAccepted, resp.StatusCode)
}
func TestRouteRequest_SourceHeaderUnknownCluster(t *testing.T) {
// When the source header doesn't match any known cluster, falls back to local client.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted)
}))
defer ts.Close()
ctrl := gomock.NewController(t)
clusterMeta := cluster.NewMockMetadata(ctrl)
clusterMeta.EXPECT().GetAllClusterInfo().Return(map[string]cluster.ClusterInformation{
"cluster-A": {ClusterID: "cluster-id-A"},
})
clusterMeta.EXPECT().GetCurrentClusterName().Return("cluster-A")
localClient := newTestFrontendHTTPClient(ts)
r, err := http.NewRequest(http.MethodPost, "http://original-host/some/path", nil)
require.NoError(t, err)
r.Header.Set(callbackSourceHeader, "unknown-cluster-id")
resp, err := routeRequest(
r,
clusterMeta,
nil,
nil,
nil,
&http.Client{},
localClient,
log.NewNoopLogger(),
)
require.NoError(t, err)
defer func() { _ = resp.Body.Close() }()
require.Equal(t, http.StatusAccepted, resp.StatusCode)
}
func TestRouteSystemCallbackRequest_NilHeaders(t *testing.T) {
// When the request has nil headers, it should fall back to the local client.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, commonnexus.PathCompletionCallbackNoIdentifier, r.URL.Path)
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
localClient := newTestFrontendHTTPClient(ts)
r := &http.Request{
Method: http.MethodPost,
URL: &url.URL{Path: "/"},
Header: nil,
}
resp, err := routeSystemCallbackRequest(
r,
nil, // clusterMetadata - not needed for nil headers path
nil, // namespaceRegistry
nil, // httpClientCache
nil, // callbackTokenGenerator
localClient,
log.NewNoopLogger(),
)
require.NoError(t, err)
defer func() { _ = resp.Body.Close() }()
require.Equal(t, http.StatusOK, resp.StatusCode)
}
func TestRouteSystemCallbackRequest_InvalidToken(t *testing.T) {
r, err := http.NewRequest(http.MethodPost, commonnexus.SystemCallbackURL, nil)
require.NoError(t, err)
r.Header.Set(commonnexus.CallbackTokenHeader, "not-valid-json")
_, err = routeSystemCallbackRequest(
r,
nil,
nil,
nil,
nil,
nil,
log.NewNoopLogger(),
)
require.Error(t, err)
var handlerErr *nexus.HandlerError
require.ErrorAs(t, err, &handlerErr)
require.Equal(t, nexus.HandlerErrorTypeBadRequest, handlerErr.Type)
require.Contains(t, handlerErr.Error(), "invalid callback token")
}
func TestRouteSystemCallbackRequest_InvalidTokenData(t *testing.T) {
// Valid token structure but invalid data field.
r, err := http.NewRequest(http.MethodPost, commonnexus.SystemCallbackURL, nil)
require.NoError(t, err)
r.Header.Set(commonnexus.CallbackTokenHeader, `{"v":1,"d":"!!!invalid-base64"}`)
tokenGen := commonnexus.NewCallbackTokenGenerator()
_, err = routeSystemCallbackRequest(
r,
nil,
nil,
nil,
tokenGen,
nil,
log.NewNoopLogger(),
)
require.Error(t, err)
var handlerErr *nexus.HandlerError
require.ErrorAs(t, err, &handlerErr)
require.Equal(t, nexus.HandlerErrorTypeBadRequest, handlerErr.Type)
}
func TestRouteSystemCallbackRequest_NamespaceNotFound(t *testing.T) {
ctrl := gomock.NewController(t)
nsRegistry := namespace.NewMockRegistry(ctrl)
tokenGen := commonnexus.NewCallbackTokenGenerator()
tokenStr, err := tokenGen.Tokenize(&tokenspb.NexusOperationCompletion{
NamespaceId: "ns-id-1",
WorkflowId: "wf-1",
RunId: "run-1",
Ref: &persistencespb.StateMachineRef{},
})
require.NoError(t, err)
nsRegistry.EXPECT().GetNamespaceByID(namespace.ID("ns-id-1")).Return(
nil, serviceerror.NewNamespaceNotFound("ns-id-1"),
)
r, err := http.NewRequest(http.MethodPost, commonnexus.SystemCallbackURL, nil)
require.NoError(t, err)
r.Header.Set(commonnexus.CallbackTokenHeader, tokenStr)
_, err = routeSystemCallbackRequest(
r,
nil,
nsRegistry,
nil,
tokenGen,
nil,
log.NewNoopLogger(),
)
require.Error(t, err)
var handlerErr *nexus.HandlerError
require.ErrorAs(t, err, &handlerErr)
require.Equal(t, nexus.HandlerErrorTypeNotFound, handlerErr.Type)
}
func TestRouteSystemCallbackRequest_Success(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, commonnexus.PathCompletionCallbackNoIdentifier, r.URL.Path)
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
ctrl := gomock.NewController(t)
clusterMeta := cluster.NewMockMetadata(ctrl)
nsRegistry := namespace.NewMockRegistry(ctrl)
tokenGen := commonnexus.NewCallbackTokenGenerator()
tokenStr, err := tokenGen.Tokenize(&tokenspb.NexusOperationCompletion{
NamespaceId: "ns-id-1",
WorkflowId: "wf-1",
RunId: "run-1",
Ref: &persistencespb.StateMachineRef{},
})
require.NoError(t, err)
testNS := namespace.NewLocalNamespaceForTest(
&persistencespb.NamespaceInfo{Id: "ns-id-1", Name: "test-ns"},
nil,
"cluster-A",
)
nsRegistry.EXPECT().GetNamespaceByID(namespace.ID("ns-id-1")).Return(testNS, nil)
// httpClientCache.Get will fail for "cluster-A", so it falls back to localClient.
clusterMeta.EXPECT().GetCurrentClusterName().Return("cluster-A").AnyTimes()
clusterMeta.EXPECT().GetAllClusterInfo().Return(map[string]cluster.ClusterInformation{}).AnyTimes()
clusterMeta.EXPECT().RegisterMetadataChangeCallback(gomock.Any(), gomock.Any())
localClient := newTestFrontendHTTPClient(ts)
// Create a cache that will fail for the requested cluster since we don't set up metadata fully.
httpClientCache := cluster.NewFrontendHTTPClientCache(clusterMeta, nil)
r, err := http.NewRequest(http.MethodPost, commonnexus.SystemCallbackURL, nil)
require.NoError(t, err)
r.Header.Set(commonnexus.CallbackTokenHeader, tokenStr)
resp, err := routeSystemCallbackRequest(
r,
clusterMeta,
nsRegistry,
httpClientCache,
tokenGen,
localClient,
log.NewNoopLogger(),
)
require.NoError(t, err)
defer func() { _ = resp.Body.Close() }()
require.Equal(t, http.StatusOK, resp.StatusCode)
}
func TestRouteRequest_SystemCallback(t *testing.T) {
// Verify that routeRequest delegates to routeSystemCallbackRequest for system callback URLs.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, commonnexus.PathCompletionCallbackNoIdentifier, r.URL.Path)
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
localClient := newTestFrontendHTTPClient(ts)
// Use nil headers to take the simplest path through routeSystemCallbackRequest.
r := &http.Request{
Method: http.MethodPost,
URL: &url.URL{
Scheme: "temporal",
Host: "system",
},
Header: nil,
}
resp, err := routeRequest(
r,
nil,
nil,
nil,
nil,
&http.Client{},
localClient,
log.NewNoopLogger(),
)
require.NoError(t, err)
defer func() { _ = resp.Body.Close() }()
require.Equal(t, http.StatusOK, resp.StatusCode)
}