func TestPluginHost_ExternalProcessPersistsPriorityThroughTaskService(t *testing.T)
The test builds the fixture binary, starts it as an external process, calls the priority webhook, and checks SQLite persistence.
Wire payload pin
func TestPluginPriorityWirePayload(t *testing.T) {
high := "high"
createPayload, err := proto.Marshal(&pluginv1.CreateTaskRequest{Priority: high})
require.NoError(t, err)
require.True(t, bytes.Contains(createPayload, []byte{0x5a, 0x04, 'h', 'i', 'g', 'h'}))
updatePayload, err := proto.Marshal(&pluginv1.UpdateTaskRequest{Priority: &high})
require.NoError(t, err)
require.True(t, bytes.Contains(updatePayload, []byte{0x32, 0x04, 'h', 'i', 'g', 'h'}))
}
External process test
func TestPluginHost_ExternalProcessPersistsPriorityThroughTaskService(t *testing.T) {
ctx := context.Background()
taskSvc, closeDB := newExternalProcessTaskService(t)
t.Cleanup(closeDB)
workspaces, err := taskSvc.ListWorkspaces(ctx)
require.NoError(t, err)
workflows, err := taskSvc.ListWorkflows(ctx, workspaces[0].ID, true)
require.NoError(t, err)
writer := &externalProcessTaskWriter{svc: taskSvc, workspaceID: workspaces[0].ID, workflowID: workflows[0].ID}
host := &pluginHost{
pluginID: "priority-probe",
capabilities: manifest.Capabilities{APIRead: []string{"tasks"}, APIWrite: []string{"tasks"}},
taskData: taskSvc,
taskWriter: writer,
}
bin := buildExternalPriorityProbe(t)
installPath := t.TempDir()
platform := goruntime.GOOS + "-" + goruntime.GOARCH
relative := filepath.Join("server", "plugin-"+platform)
destination := filepath.Join(installPath, relative)
require.NoError(t, os.MkdirAll(filepath.Dir(destination), 0o755))
contents, err := os.ReadFile(bin)
require.NoError(t, err)
require.NoError(t, os.WriteFile(destination, contents, 0o755))
log, err := logger.NewLogger(logger.LoggingConfig{Level: "error", Format: "json", OutputPath: "stdout"})
require.NoError(t, err)
runtime := pluginruntime.NewManager(t.TempDir(), nil, log)
t.Cleanup(runtime.StopAll)
record := &store.Record{Manifest: manifest.Manifest{
ID: "priority-probe", APIVersion: 1, Version: "1.0.0",
Runtime: manifest.Runtime{Type: "binary", Executables: map[string]string{platform: relative}},
}, InstallPath: installPath}
require.NoError(t, runtime.Start(ctx, record, func(string) pluginsdk.Host { return host }))
remote, ok := runtime.Get(record.ID)
require.True(t, ok)
var response *pluginsdk.WebhookResponse
require.Eventually(t, func() bool {
var callErr error
response, callErr = remote.HandleWebhook(ctx, &pluginsdk.WebhookRequest{WebhookKey: "priority"})
return callErr == nil && string(response.Body) != "no host"
}, 5*time.Second, 20*time.Millisecond, "the plugin must receive its Host before the priority probe")
require.Equalf(t, int32(200), response.Status, "external plugin response: %s", response.Body)
var probe externalPriorityProbeResult
require.NoError(t, json.Unmarshal(response.Body, &probe), "plugin response must expose immediate Host readback")
require.Equal(t, "high", probe.CreateHighReadback)
require.Equal(t, "medium", probe.CreateDefaultReadback)
require.Equal(t, "high", probe.UpdateHighReadback)
require.NotEmpty(t, probe.InvalidCreateError)
require.NotEmpty(t, probe.InvalidUpdateError)
require.Equal(t, []string{"high", ""}, writer.createPriorities)
require.Equal(t, []string{"high"}, writer.updatePriorities)
}
Task writer adapter
type externalProcessTaskWriter struct {
svc *taskservice.Service
workspaceID string
workflowID string
createPriorities []string
updatePriorities []string
}
func (w *externalProcessTaskWriter) CreateTask(ctx context.Context, in TaskCreateInput) (*taskmodels.Task, error) {
w.createPriorities = append(w.createPriorities, in.Priority)
result, err := w.svc.CreateTask(ctx, &taskservice.CreateTaskRequest{
WorkspaceID: w.workspaceID, WorkflowID: w.workflowID, WorkflowStepID: in.WorkflowStepID,
Title: in.Title, Description: in.Description, ParentID: in.ParentID, Metadata: in.Metadata,
PlanMode: in.PlanMode, Priority: in.Priority, StartAgent: in.StartAgent,
})
return result.Task, err
}
func (w *externalProcessTaskWriter) UpdateTask(ctx context.Context, in TaskUpdateInput) (*taskmodels.Task, error) {
if in.Priority != nil {
w.updatePriorities = append(w.updatePriorities, *in.Priority)
}
return w.svc.UpdateTask(ctx, in.ID, &taskservice.UpdateTaskRequest{
Title: in.Title, Description: in.Description, Priority: in.Priority,
})
}