ComposerGeneratorTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <?php
  2. namespace SixShop\MakerBundle\Tests\Unit\Generator;
  3. use PHPUnit\Framework\TestCase;
  4. use SixShop\MakerBundle\Generator\ComposerGenerator;
  5. use Symfony\Component\Console\Style\SymfonyStyle;
  6. use Symfony\Component\Console\Input\InputInterface;
  7. use Symfony\Component\Console\Output\OutputInterface;
  8. class ComposerGeneratorTest extends TestCase
  9. {
  10. private $composerGenerator;
  11. private $io;
  12. private $testDir;
  13. protected function setUp(): void
  14. {
  15. $this->testDir = sys_get_temp_dir() . '/composer_test_' . uniqid();
  16. mkdir($this->testDir, 0777, true);
  17. // Create mock SymfonyStyle
  18. $this->io = $this->createMock(SymfonyStyle::class);
  19. $this->composerGenerator = new ComposerGenerator();
  20. }
  21. protected function tearDown(): void
  22. {
  23. if (is_dir($this->testDir)) {
  24. $this->removeDirectory($this->testDir);
  25. }
  26. }
  27. private function removeDirectory($dir)
  28. {
  29. if (!is_dir($dir)) {
  30. return;
  31. }
  32. $items = scandir($dir);
  33. foreach ($items as $item) {
  34. if ($item != '.' && $item != '..') {
  35. $path = $dir . DIRECTORY_SEPARATOR . $item;
  36. if (is_dir($path)) {
  37. $this->removeDirectory($path);
  38. } else {
  39. unlink($path);
  40. }
  41. }
  42. }
  43. rmdir($dir);
  44. }
  45. public function testGenerateContent()
  46. {
  47. $packageName = 'six-shop/test-extension';
  48. $namespace = 'SixShop\\TestExtension\\';
  49. $id = 'six-shop-test-extension';
  50. $description = 'A test extension package';
  51. $content = $this->composerGenerator->generateContent($packageName, $namespace, $id, $description);
  52. $this->assertIsString($content);
  53. $this->assertNotEmpty($content);
  54. // Test JSON structure
  55. $jsonData = json_decode($content, true);
  56. $this->assertIsArray($jsonData);
  57. $this->assertEquals(JSON_ERROR_NONE, json_last_error());
  58. // Test package information
  59. $this->assertEquals($packageName, $jsonData['name']);
  60. $this->assertEquals($description, $jsonData['description']);
  61. // Test PSR-4 autoloading
  62. $this->assertArrayHasKey('autoload', $jsonData);
  63. $this->assertArrayHasKey('psr-4', $jsonData['autoload']);
  64. $this->assertArrayHasKey($namespace, $jsonData['autoload']['psr-4']);
  65. $this->assertEquals('src', $jsonData['autoload']['psr-4'][$namespace]);
  66. // Test SixShop extension configuration
  67. $this->assertArrayHasKey('extra', $jsonData);
  68. $this->assertArrayHasKey('sixshop', $jsonData['extra']);
  69. $this->assertEquals($id, $jsonData['extra']['sixshop']['id']);
  70. }
  71. public function testSaveComposerFile()
  72. {
  73. $packageName = 'six-shop/test-extension';
  74. $content = '{"name": "six-shop/test-extension", "description": "Test"}';
  75. $this->io->expects($this->once())
  76. ->method('success')
  77. ->with($this->stringContains('composer.json 文件已成功生成'));
  78. $result = $this->composerGenerator->saveComposerFile($packageName, $content, $this->testDir, $this->io);
  79. $this->assertTrue($result);
  80. $this->assertFileExists($this->testDir . '/composer.json');
  81. $this->assertEquals($content, file_get_contents($this->testDir . '/composer.json'));
  82. }
  83. public function testSaveComposerFileFailure()
  84. {
  85. $packageName = 'six-shop/test-extension';
  86. $content = '{"name": "six-shop/test-extension"}';
  87. $invalidDir = '/invalid/directory/path';
  88. $this->io->expects($this->once())
  89. ->method('error')
  90. ->with($this->stringContains('无法创建目录'));
  91. $result = $this->composerGenerator->saveComposerFile($packageName, $content, $invalidDir, $this->io);
  92. $this->assertFalse($result);
  93. }
  94. public function testCreateComposerJson()
  95. {
  96. $packageName = 'six-shop/test-extension';
  97. $namespace = 'SixShop\\TestExtension\\';
  98. $id = 'six-shop-test-extension';
  99. $description = 'A test extension package';
  100. $this->io->expects($this->once())
  101. ->method('section')
  102. ->with('生成的 composer.json 内容:');
  103. $this->io->expects($this->once())
  104. ->method('text')
  105. ->with($this->isType('string'));
  106. $this->io->expects($this->once())
  107. ->method('confirm')
  108. ->with('是否将内容保存到 composer.json 文件?', true)
  109. ->willReturn(true);
  110. $this->io->expects($this->once())
  111. ->method('success')
  112. ->with($this->stringContains('composer.json 文件已成功生成'));
  113. $result = $this->composerGenerator->createComposerJson(
  114. $packageName, $namespace, $id, $description, $this->testDir, $this->io
  115. );
  116. $this->assertTrue($result);
  117. $this->assertFileExists($this->testDir . '/composer.json');
  118. }
  119. public function testCreateComposerJsonWithUserDecline()
  120. {
  121. $packageName = 'six-shop/test-extension';
  122. $namespace = 'SixShop\\TestExtension\\';
  123. $id = 'six-shop-test-extension';
  124. $description = 'A test extension package';
  125. $this->io->expects($this->once())
  126. ->method('confirm')
  127. ->with('是否将内容保存到 composer.json 文件?', true)
  128. ->willReturn(false);
  129. $result = $this->composerGenerator->createComposerJson(
  130. $packageName, $namespace, $id, $description, $this->testDir, $this->io
  131. );
  132. $this->assertTrue($result);
  133. $this->assertFileDoesNotExist($this->testDir . '/composer.json');
  134. }
  135. public function testGenerateContentWithDifferentNamespaces()
  136. {
  137. $testCases = [
  138. ['SixShop\\Hello\\', 'six-shop/hello'],
  139. ['MyCompany\\Extensions\\TestExt\\', 'mycompany/test-extension'],
  140. ['SingleNamespace\\', 'vendor/package']
  141. ];
  142. foreach ($testCases as [$namespace, $packageName]) {
  143. $content = $this->composerGenerator->generateContent($packageName, $namespace, 'test-id', 'Test description');
  144. $jsonData = json_decode($content, true);
  145. $this->assertArrayHasKey($namespace, $jsonData['autoload']['psr-4']);
  146. $this->assertEquals('src', $jsonData['autoload']['psr-4'][$namespace]);
  147. }
  148. }
  149. public function testJsonStructureCompliance()
  150. {
  151. $content = $this->composerGenerator->generateContent(
  152. 'six-shop/test-extension',
  153. 'SixShop\\TestExtension\\',
  154. 'test-extension-id',
  155. 'Test description'
  156. );
  157. $jsonData = json_decode($content, true);
  158. // Test required composer.json fields
  159. $this->assertArrayHasKey('name', $jsonData);
  160. $this->assertArrayHasKey('description', $jsonData);
  161. $this->assertArrayHasKey('type', $jsonData);
  162. $this->assertArrayHasKey('require', $jsonData);
  163. $this->assertArrayHasKey('autoload', $jsonData);
  164. $this->assertArrayHasKey('extra', $jsonData);
  165. // Test type field
  166. $this->assertEquals('sixshop-extension', $jsonData['type']);
  167. // Test minimum requirements
  168. $this->assertArrayHasKey('php', $jsonData['require']);
  169. $this->assertEquals('>=8.3', $jsonData['require']['php']);
  170. }
  171. public function testExtensionIdFormat()
  172. {
  173. $testCases = [
  174. 'six-shop/hello' => 'six-shop-hello',
  175. 'vendor/my-extension' => 'vendor-my-extension',
  176. 'company/test_pkg' => 'company-test_pkg'
  177. ];
  178. foreach ($testCases as $packageName => $expectedId) {
  179. $content = $this->composerGenerator->generateContent(
  180. $packageName,
  181. 'Test\\Namespace\\',
  182. $expectedId,
  183. 'Test description'
  184. );
  185. $jsonData = json_decode($content, true);
  186. $this->assertEquals($expectedId, $jsonData['extra']['sixshop']['id']);
  187. }
  188. }
  189. public function testGenerateContentErrorHandling()
  190. {
  191. // Test that the method works with valid parameters
  192. // Since the template exists, this should not throw an exception
  193. $content = $this->composerGenerator->generateContent(
  194. 'test/package',
  195. 'Test\\Namespace\\',
  196. 'test-id',
  197. 'Test description'
  198. );
  199. $this->assertIsString($content);
  200. $this->assertNotEmpty($content);
  201. }
  202. public function testSaveComposerFileDirectoryCreation()
  203. {
  204. $nestedDir = $this->testDir . '/nested/deep/directory';
  205. $packageName = 'six-shop/test-extension';
  206. $content = '{"name": "six-shop/test-extension"}';
  207. $this->io->expects($this->once())
  208. ->method('success');
  209. $result = $this->composerGenerator->saveComposerFile($packageName, $content, $nestedDir, $this->io);
  210. $this->assertTrue($result);
  211. $this->assertDirectoryExists($nestedDir);
  212. $this->assertFileExists($nestedDir . '/composer.json');
  213. }
  214. public function testCompletePackageJsonStructure()
  215. {
  216. $content = $this->composerGenerator->generateContent(
  217. 'six-shop/advanced-extension',
  218. 'SixShop\\AdvancedExtension\\',
  219. 'six-shop-advanced-extension',
  220. 'An advanced SixShop extension with comprehensive features'
  221. );
  222. $jsonData = json_decode($content, true);
  223. // Verify complete structure
  224. $expectedStructure = [
  225. 'name' => 'six-shop/advanced-extension',
  226. 'description' => 'An advanced SixShop extension with comprehensive features',
  227. 'type' => 'sixshop-extension',
  228. 'require' => [
  229. 'php' => '>=8.3',
  230. 'six-shop/core' => '>=0.6 <1.0'
  231. ],
  232. 'autoload' => [
  233. 'psr-4' => [
  234. 'SixShop\\AdvancedExtension\\' => 'src'
  235. ]
  236. ],
  237. 'extra' => [
  238. 'sixshop' => [
  239. 'id' => 'six-shop-advanced-extension',
  240. 'class' => 'SixShop\\AdvancedExtension\\Extension'
  241. ]
  242. ]
  243. ];
  244. $this->assertEquals($expectedStructure['name'], $jsonData['name']);
  245. $this->assertEquals($expectedStructure['description'], $jsonData['description']);
  246. $this->assertEquals($expectedStructure['type'], $jsonData['type']);
  247. $this->assertEquals($expectedStructure['require'], $jsonData['require']);
  248. $this->assertEquals($expectedStructure['autoload'], $jsonData['autoload']);
  249. $this->assertEquals($expectedStructure['extra'], $jsonData['extra']);
  250. }
  251. }