From a09b46089575fce1bcb7d73f80f3910ec1f685b6 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Wed, 16 Apr 2025 14:58:46 +0200
Subject: [PATCH] Issue #2797421 by dcam, mikeryan, chx, id.conky,
 mkalkbrenner, mikelutz: Sourceless migration plugins are broken

(cherry picked from commit c42f5d9328c3d33a39c2bfbee383547818d1640b)
---
 .../src/Plugin/NoSourcePluginDecorator.php    |  2 +-
 .../Plugin/NoSourcePluginDecoratorTest.php    | 78 +++++++++++++++++++
 2 files changed, 79 insertions(+), 1 deletion(-)
 create mode 100644 core/modules/migrate/tests/src/Unit/Plugin/NoSourcePluginDecoratorTest.php

diff --git a/core/modules/migrate/src/Plugin/NoSourcePluginDecorator.php b/core/modules/migrate/src/Plugin/NoSourcePluginDecorator.php
index aa2121eee2ec..98a0c7cbc827 100644
--- a/core/modules/migrate/src/Plugin/NoSourcePluginDecorator.php
+++ b/core/modules/migrate/src/Plugin/NoSourcePluginDecorator.php
@@ -36,7 +36,7 @@ public function getDefinitions() {
     /** @var \Drupal\Component\Plugin\PluginManagerInterface $source_plugin_manager */
     $source_plugin_manager = \Drupal::service('plugin.manager.migrate.source');
     return array_filter($this->decorated->getDefinitions(), function (array $definition) use ($source_plugin_manager) {
-      return $source_plugin_manager->hasDefinition($definition['source']['plugin']);
+      return !empty($definition['source']['plugin']) && $source_plugin_manager->hasDefinition($definition['source']['plugin']);
     });
   }
 
diff --git a/core/modules/migrate/tests/src/Unit/Plugin/NoSourcePluginDecoratorTest.php b/core/modules/migrate/tests/src/Unit/Plugin/NoSourcePluginDecoratorTest.php
new file mode 100644
index 000000000000..aa1a50e3351d
--- /dev/null
+++ b/core/modules/migrate/tests/src/Unit/Plugin/NoSourcePluginDecoratorTest.php
@@ -0,0 +1,78 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\migrate\Unit\Plugin;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\migrate\Plugin\MigrateSourcePluginManager;
+use Drupal\migrate\Plugin\NoSourcePluginDecorator;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\migrate\Plugin\NoSourcePluginDecorator
+ * @group migrate
+ */
+class NoSourcePluginDecoratorTest extends UnitTestCase {
+
+  /**
+   * @covers ::getDefinitions
+   * @dataProvider providerGetDefinitions
+   */
+  public function testGetDefinitions(array $definition, bool $source_exists): void {
+    $source_manager = $this->createMock(MigrateSourcePluginManager::class);
+    $source_manager->expects($this->any())
+      ->method('hasDefinition')
+      ->willReturn($source_exists);
+    $container = new ContainerBuilder();
+    $container->set('plugin.manager.migrate.source', $source_manager);
+    \Drupal::setContainer($container);
+
+    $discovery_interface = $this->createMock(DiscoveryInterface::class);
+    $discovery_interface->expects($this->once())
+      ->method('getDefinitions')
+      ->willReturn([$definition]);
+
+    $decorator = new NoSourcePluginDecorator($discovery_interface);
+    $results = $decorator->getDefinitions();
+    if ($source_exists) {
+      $this->assertEquals([$definition], $results);
+    }
+    else {
+      $this->assertEquals([], $results);
+    }
+  }
+
+  /**
+   * Provides data for testGetDefinitions().
+   */
+  public static function providerGetDefinitions(): array {
+    return [
+      'source exists' => [
+        [
+          'source' => ['plugin' => 'valid_plugin'],
+          'process' => [],
+          'destination' => [],
+        ],
+        TRUE,
+      ],
+      'source does not exist' => [
+        [
+          'source' => ['plugin' => 'invalid_plugin'],
+          'process' => [],
+          'destination' => [],
+        ],
+        FALSE,
+      ],
+      'source is not defined' => [
+        [
+          'process' => [],
+          'destination' => [],
+        ],
+        FALSE,
+      ],
+    ];
+  }
+
+}
-- 
GitLab