package pluginhost import ( "errors" "testing" pluginv1 "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1" sdkruntime "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/runtime" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) // makeTestClient constructs a Client with the given declared capabilities. // It creates a lazy (non-connecting) gRPC ClientConn so that accessors which // call c.rpc.() do not panic — no real connection is made. func makeTestClient(t *testing.T, capabilities []*pluginv1.CapabilityDescriptor) *Client { t.Helper() conn, err := grpc.NewClient("passthrough:///bufnet", grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { t.Fatalf("grpc.NewClient: %v", err) } t.Cleanup(func() { _ = conn.Close() }) rpc := sdkruntime.NewClient(conn) manifest := &pluginv1.PluginManifest{Capabilities: capabilities} return newClient(0, rpc, manifest) } func TestClient_ScheduledTask_CapabilityGate(t *testing.T) { t.Run("absent capability returns error", func(t *testing.T) { c := makeTestClient(t, nil) _, err := c.ScheduledTask("missing") if err == nil { t.Fatal("expected error for missing scheduled_task.v1 capability, got nil") } if !errors.Is(err, ErrCapabilityNotFound) { t.Errorf("expected ErrCapabilityNotFound, got %v", err) } }) t.Run("declared capability returns no error", func(t *testing.T) { c := makeTestClient(t, []*pluginv1.CapabilityDescriptor{ {Type: "scheduled_task.v1", Id: "nightly"}, }) got, err := c.ScheduledTask("nightly") if err != nil { t.Fatalf("unexpected error: %v", err) } if got == nil { t.Fatal("expected non-nil ScheduledTaskClient") } }) t.Run("wrong id returns error", func(t *testing.T) { c := makeTestClient(t, []*pluginv1.CapabilityDescriptor{ {Type: "scheduled_task.v1", Id: "nightly"}, }) _, err := c.ScheduledTask("weekly") if err == nil { t.Fatal("expected error for mismatched scheduled_task.v1 capability id, got nil") } if !errors.Is(err, ErrCapabilityNotFound) { t.Errorf("expected ErrCapabilityNotFound, got %v", err) } }) } func TestClient_ScanSource_CapabilityGate(t *testing.T) { t.Run("absent capability returns error", func(t *testing.T) { c := makeTestClient(t, nil) _, err := c.ScanSource("missing") if err == nil { t.Fatal("expected error for missing scan_source.v1 capability, got nil") } if !errors.Is(err, ErrCapabilityNotFound) { t.Errorf("expected ErrCapabilityNotFound, got %v", err) } }) t.Run("declared capability returns no error", func(t *testing.T) { c := makeTestClient(t, []*pluginv1.CapabilityDescriptor{ {Type: "scan_source.v1", Id: "arr"}, }) got, err := c.ScanSource("arr") if err != nil { t.Fatalf("unexpected error: %v", err) } if got == nil { t.Fatal("expected non-nil ScanSourceClient") } }) t.Run("wrong id returns error", func(t *testing.T) { c := makeTestClient(t, []*pluginv1.CapabilityDescriptor{ {Type: "scan_source.v1", Id: "arr"}, }) _, err := c.ScanSource("inotify") if err == nil { t.Fatal("expected error for mismatched scan_source.v1 capability id, got nil") } if !errors.Is(err, ErrCapabilityNotFound) { t.Errorf("expected ErrCapabilityNotFound, got %v", err) } }) } func TestClient_MarkerProvider_CapabilityGate(t *testing.T) { t.Run("absent capability returns error", func(t *testing.T) { c := makeTestClient(t, nil) _, err := c.MarkerProvider("missing") if err == nil { t.Fatal("expected error for missing marker_provider.v1 capability, got nil") } if !errors.Is(err, ErrCapabilityNotFound) { t.Errorf("expected ErrCapabilityNotFound, got %v", err) } }) t.Run("declared capability returns no error", func(t *testing.T) { c := makeTestClient(t, []*pluginv1.CapabilityDescriptor{ {Type: "marker_provider.v1", Id: "markers"}, }) got, err := c.MarkerProvider("markers") if err != nil { t.Fatalf("unexpected error: %v", err) } if got == nil { t.Fatal("expected non-nil MarkerProviderClient") } }) t.Run("wrong id returns error", func(t *testing.T) { c := makeTestClient(t, []*pluginv1.CapabilityDescriptor{ {Type: "marker_provider.v1", Id: "markers"}, }) _, err := c.MarkerProvider("other") if err == nil { t.Fatal("expected error for mismatched marker_provider.v1 capability id, got nil") } if !errors.Is(err, ErrCapabilityNotFound) { t.Errorf("expected ErrCapabilityNotFound, got %v", err) } }) } func TestClient_ImageResolver_CapabilityGate(t *testing.T) { t.Run("absent capability returns error", func(t *testing.T) { c := makeTestClient(t, nil) _, err := c.ImageResolver("missing") if err == nil { t.Fatal("expected error for missing image_resolver.v1 capability, got nil") } if !errors.Is(err, ErrCapabilityNotFound) { t.Errorf("expected ErrCapabilityNotFound, got %v", err) } }) t.Run("declared capability returns no error", func(t *testing.T) { c := makeTestClient(t, []*pluginv1.CapabilityDescriptor{ {Type: "image_resolver.v1", Id: "tmdb"}, }) got, err := c.ImageResolver("tmdb") if err != nil { t.Fatalf("unexpected error: %v", err) } if got == nil { t.Fatal("expected non-nil ImageResolverClient") } }) t.Run("wrong id returns error", func(t *testing.T) { c := makeTestClient(t, []*pluginv1.CapabilityDescriptor{ {Type: "image_resolver.v1", Id: "tmdb"}, }) _, err := c.ImageResolver("imdb") if err == nil { t.Fatal("expected error for mismatched image_resolver.v1 capability id, got nil") } if !errors.Is(err, ErrCapabilityNotFound) { t.Errorf("expected ErrCapabilityNotFound, got %v", err) } }) }