Files
kandev/apps/backend/pkg/pluginsdk/protocol_test.go
T

103 lines
5.1 KiB
Go

package pluginsdk
import (
"testing"
pluginv1 "github.com/kandev/kandev/proto/kandev/plugin/v1"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
)
func TestProtocolDeclaresAdditivePluginAndHostMethods(t *testing.T) {
services := pluginv1.File_kandev_plugin_v1_plugin_proto.Services()
assertServiceMethods(t, services.ByName("Plugin"), "HandleAction", "SearchEntityReferences", "AuthorizeEntityReference", "ResolveGitCredential", "GetGitCredentialBinding")
assertServiceMethods(t, services.ByName("Host"), "ListExecutorProfiles", "PreviewPluginOwnedTaskTree", "DeletePluginOwnedTaskTree")
assertMessageFields(t, "PluginActionRequest", "action_key", "context", "body")
assertMessageFields(t, "PluginActionResponse", "body", "headers", "status")
assertMessageFields(t, "VerifiedActionContext", "actor_id", "workspace_id", "task_id", "repository_id", "session_id", "head_branch")
assertMessageFields(t, "SearchEntityReferencesRequest", "source", "workspace_id", "query", "limit")
assertMessageFields(t, "AuthorizeEntityReferenceRequest", "source", "workspace_id", "purpose", "reference")
assertMessageFields(t, "ResolveGitCredentialRequest", "provider_id", "workspace_id", "task_id", "session_id", "repository_id", "host", "path")
assertMessageFields(t, "GitCredentialBindingRequest", "provider_id", "workspace_id", "task_id", "session_id", "repository_id", "host", "path")
assertMessageFields(t, "GitCredentialBindingResponse", "binding")
assertMessageFields(t, "TaskRepository", "id", "repository_id", "base_branch", "position", "checkout_branch")
assertMessageFields(t, "Repository", "id", "workspace_id", "name", "default_branch", "source_type", "provider_id", "provider_repository_id", "provider_host", "owner_or_project", "provider_name", "remote_url", "provider_scope")
assertMessageOmitsFields(t, "Repository", "local_path", "setup_script", "cleanup_script", "dev_script", "copy_files")
assertMessageFields(t, "Task", "priority", "labels")
assertDeprecatedRepeatedMessageField(t, "Task", "labels", 23)
assertMessageFields(t, "CreateTaskRequest", "repositories", "launch", "metadata", "priority")
assertMessageFields(t, "UpdateTaskRequest", "priority")
assertMessageOmitsFields(t, "CreateTaskRequest", "labels")
assertMessageOmitsFields(t, "UpdateTaskRequest", "labels")
assertMessageFields(t, "RemoteRepositoryDescriptor", "provider_id", "provider_host", "owner_or_project", "provider_repository_id", "name", "clone_url", "provider_scope")
assertMessageFields(t, "DeletePluginOwnedTaskTreeProgress", "deleted_task_ids")
}
func TestReleasedTaskLabelsGeneratedSourceCompatibility(t *testing.T) {
generated := &pluginv1.Task{Labels: []string{"legacy"}}
native := Task{Labels: generated.GetLabels()} //nolint:staticcheck // verifies the released API v1 source surface
if len(native.Labels) != 1 || native.Labels[0] != "legacy" {
t.Fatalf("released Task.Labels source surface did not round-trip: %+v", native.Labels)
}
}
func assertServiceMethods(t *testing.T, service protoreflect.ServiceDescriptor, methods ...protoreflect.Name) {
t.Helper()
if service == nil {
t.Fatal("required service is missing")
}
for _, method := range methods {
if service.Methods().ByName(method) == nil {
t.Fatalf("%s service is missing %s", service.FullName(), method)
}
}
}
func assertMessageFields(t *testing.T, messageName string, fields ...protoreflect.Name) {
t.Helper()
message := pluginv1.File_kandev_plugin_v1_plugin_proto.Messages().ByName(protoreflect.Name(messageName))
if message == nil {
t.Fatalf("required message %s is missing", messageName)
}
for _, field := range fields {
if message.Fields().ByName(field) == nil {
t.Fatalf("%s is missing field %s", message.FullName(), field)
}
}
}
func assertMessageOmitsFields(t *testing.T, messageName string, fields ...protoreflect.Name) {
t.Helper()
message := pluginv1.File_kandev_plugin_v1_plugin_proto.Messages().ByName(protoreflect.Name(messageName))
if message == nil {
t.Fatalf("required message %s is missing", messageName)
}
for _, field := range fields {
if message.Fields().ByName(field) != nil {
t.Fatalf("%s must not expose field %s", message.FullName(), field)
}
}
}
func assertDeprecatedRepeatedMessageField(t *testing.T, messageName, fieldName string, number protoreflect.FieldNumber) {
t.Helper()
message := pluginv1.File_kandev_plugin_v1_plugin_proto.Messages().ByName(protoreflect.Name(messageName))
if message == nil {
t.Fatalf("required message %s is missing", messageName)
}
field := message.Fields().ByName(protoreflect.Name(fieldName))
if field == nil {
t.Fatalf("%s is missing field %s", message.FullName(), fieldName)
}
if field.Number() != number {
t.Fatalf("%s.%s field number = %d, want %d", message.FullName(), fieldName, field.Number(), number)
}
if field.Cardinality() != protoreflect.Repeated {
t.Fatalf("%s.%s cardinality = %s, want repeated", message.FullName(), fieldName, field.Cardinality())
}
options, ok := field.Options().(*descriptorpb.FieldOptions)
if !ok || !options.GetDeprecated() {
t.Fatalf("%s.%s must remain deprecated", message.FullName(), fieldName)
}
}