|
5 | 5 | use App\Features\ShowAuthButtons; |
6 | 6 | use App\Features\ShowPlugins; |
7 | 7 | use App\Livewire\Customer\Plugins\Create; |
| 8 | +use App\Models\Plugin; |
8 | 9 | use App\Models\User; |
9 | 10 | use Illuminate\Foundation\Testing\RefreshDatabase; |
| 11 | +use Illuminate\Support\Facades\Http; |
10 | 12 | use Laravel\Pennant\Feature; |
11 | 13 | use Livewire\Livewire; |
12 | 14 | use Tests\TestCase; |
@@ -42,6 +44,22 @@ private function sampleRepos(): array |
42 | 44 | ]; |
43 | 45 | } |
44 | 46 |
|
| 47 | + private function fakeComposerJson(string $owner, string $repo, string $packageName): void |
| 48 | + { |
| 49 | + $composerJson = base64_encode(json_encode(['name' => $packageName])); |
| 50 | + |
| 51 | + Http::fake([ |
| 52 | + "api.github.com/repos/{$owner}/{$repo}/contents/composer.json*" => Http::response([ |
| 53 | + 'content' => $composerJson, |
| 54 | + ]), |
| 55 | + 'api.github.com/*' => Http::response([], 404), |
| 56 | + ]); |
| 57 | + } |
| 58 | + |
| 59 | + // ======================================== |
| 60 | + // Owner/Repository Selection Tests |
| 61 | + // ======================================== |
| 62 | + |
45 | 63 | public function test_owners_are_extracted_from_repositories(): void |
46 | 64 | { |
47 | 65 | $user = $this->createGitHubUser(); |
@@ -113,4 +131,90 @@ public function test_no_owner_selected_returns_empty_repositories(): void |
113 | 131 |
|
114 | 132 | $this->assertEmpty($ownerRepos); |
115 | 133 | } |
| 134 | + |
| 135 | + // ======================================== |
| 136 | + // Namespace Validation Tests |
| 137 | + // ======================================== |
| 138 | + |
| 139 | + public function test_submission_blocked_when_namespace_claimed_by_another_user(): void |
| 140 | + { |
| 141 | + $existingUser = User::factory()->create(); |
| 142 | + Plugin::factory()->for($existingUser)->create(['name' => 'acme/existing-plugin']); |
| 143 | + |
| 144 | + $user = $this->createGitHubUser(); |
| 145 | + |
| 146 | + $this->fakeComposerJson('acme', 'new-plugin', 'acme/new-plugin'); |
| 147 | + |
| 148 | + Livewire::actingAs($user)->test(Create::class) |
| 149 | + ->set('repository', 'acme/new-plugin') |
| 150 | + ->set('pluginType', 'free') |
| 151 | + ->call('submitPlugin') |
| 152 | + ->assertNoRedirect(); |
| 153 | + |
| 154 | + $this->assertDatabaseMissing('plugins', [ |
| 155 | + 'repository_url' => 'https://github.com/acme/new-plugin', |
| 156 | + ]); |
| 157 | + } |
| 158 | + |
| 159 | + public function test_submission_blocked_for_reserved_namespace(): void |
| 160 | + { |
| 161 | + $user = $this->createGitHubUser(); |
| 162 | + |
| 163 | + $this->fakeComposerJson('nativephp', 'my-plugin', 'nativephp/my-plugin'); |
| 164 | + |
| 165 | + Livewire::actingAs($user)->test(Create::class) |
| 166 | + ->set('repository', 'nativephp/my-plugin') |
| 167 | + ->set('pluginType', 'free') |
| 168 | + ->call('submitPlugin') |
| 169 | + ->assertNoRedirect(); |
| 170 | + |
| 171 | + $this->assertDatabaseMissing('plugins', [ |
| 172 | + 'repository_url' => 'https://github.com/nativephp/my-plugin', |
| 173 | + ]); |
| 174 | + } |
| 175 | + |
| 176 | + public function test_submission_allowed_for_own_namespace(): void |
| 177 | + { |
| 178 | + $user = $this->createGitHubUser(); |
| 179 | + Plugin::factory()->for($user)->create(['name' => 'myvendor/first-plugin']); |
| 180 | + |
| 181 | + $composerJson = base64_encode(json_encode(['name' => 'myvendor/second-plugin'])); |
| 182 | + |
| 183 | + Http::fake([ |
| 184 | + 'api.github.com/repos/myvendor/second-plugin/contents/composer.json*' => Http::response([ |
| 185 | + 'content' => $composerJson, |
| 186 | + ]), |
| 187 | + 'api.github.com/repos/myvendor/second-plugin/hooks' => Http::response(['id' => 1]), |
| 188 | + 'api.github.com/*' => Http::response([], 404), |
| 189 | + ]); |
| 190 | + |
| 191 | + Livewire::actingAs($user)->test(Create::class) |
| 192 | + ->set('repository', 'myvendor/second-plugin') |
| 193 | + ->set('pluginType', 'free') |
| 194 | + ->call('submitPlugin'); |
| 195 | + |
| 196 | + $this->assertDatabaseHas('plugins', [ |
| 197 | + 'repository_url' => 'https://github.com/myvendor/second-plugin', |
| 198 | + 'user_id' => $user->id, |
| 199 | + ]); |
| 200 | + } |
| 201 | + |
| 202 | + public function test_submission_blocked_when_composer_json_missing(): void |
| 203 | + { |
| 204 | + $user = $this->createGitHubUser(); |
| 205 | + |
| 206 | + Http::fake([ |
| 207 | + 'api.github.com/*' => Http::response([], 404), |
| 208 | + ]); |
| 209 | + |
| 210 | + Livewire::actingAs($user)->test(Create::class) |
| 211 | + ->set('repository', 'testuser/no-composer') |
| 212 | + ->set('pluginType', 'free') |
| 213 | + ->call('submitPlugin') |
| 214 | + ->assertNoRedirect(); |
| 215 | + |
| 216 | + $this->assertDatabaseMissing('plugins', [ |
| 217 | + 'repository_url' => 'https://github.com/testuser/no-composer', |
| 218 | + ]); |
| 219 | + } |
116 | 220 | } |
0 commit comments