194 lines
8.7 KiB
PHP
194 lines
8.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\EtsyConnection;
|
|
use App\Models\Project;
|
|
use App\Models\ProjectListingLink;
|
|
use App\Models\ProjectRelease;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Tests\TestCase;
|
|
|
|
class ProjectListingLinkTest extends TestCase
|
|
{
|
|
use LazilyRefreshDatabase;
|
|
|
|
private function connectEtsy(): EtsyConnection
|
|
{
|
|
config()->set('services.etsy', ['keystring' => 'key', 'shared_secret' => 'secret', 'shop_name' => 'HickLab3D']);
|
|
|
|
return EtsyConnection::create([
|
|
'shop_id' => 42, 'shop_name' => 'HickLab3D', 'access_token' => 'token', 'refresh_token' => 'refresh',
|
|
'expires_at' => now()->addHour(), 'scopes' => 'listings_r listings_w transactions_r transactions_w shops_r',
|
|
]);
|
|
}
|
|
|
|
public function test_admin_can_attach_a_verified_listing_by_reading_it_from_etsy(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$this->connectEtsy();
|
|
Http::fake(['https://api.etsy.com/v3/application/listings/999*' => Http::response([
|
|
'listing_id' => 999, 'shop_id' => 42, 'title' => 'Dragon figurine', 'state' => 'active',
|
|
'last_modified_timestamp' => now()->timestamp,
|
|
])]);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$project = Project::factory()->create();
|
|
|
|
$this->actingAs($admin)->post(route('projects.listings.store', $project), ['listing_id' => 999])
|
|
->assertRedirect();
|
|
|
|
$this->assertDatabaseHas('project_listing_links', [
|
|
'project_id' => $project->id, 'shop_id' => 42, 'listing_id' => 999, 'title_snapshot' => 'Dragon figurine', 'state_snapshot' => 'active',
|
|
]);
|
|
}
|
|
|
|
public function test_rejects_a_listing_that_belongs_to_a_different_shop(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$this->connectEtsy();
|
|
Http::fake(['https://api.etsy.com/v3/application/listings/555*' => Http::response([
|
|
'listing_id' => 555, 'shop_id' => 999999, 'title' => 'Not ours', 'state' => 'active',
|
|
])]);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$project = Project::factory()->create();
|
|
|
|
$this->actingAs($admin)->post(route('projects.listings.store', $project), ['listing_id' => 555])
|
|
->assertSessionHasErrors('listing_id');
|
|
$this->assertDatabaseCount('project_listing_links', 0);
|
|
}
|
|
|
|
public function test_rejects_a_nonexistent_listing(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$this->connectEtsy();
|
|
Http::fake(['https://api.etsy.com/v3/application/listings/404*' => Http::response(['error' => 'not found'], 404)]);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$project = Project::factory()->create();
|
|
|
|
$this->actingAs($admin)->post(route('projects.listings.store', $project), ['listing_id' => 404])
|
|
->assertSessionHasErrors('listing_id');
|
|
}
|
|
|
|
public function test_cannot_attach_the_same_listing_to_a_different_project(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$this->connectEtsy();
|
|
Http::fake(['https://api.etsy.com/v3/application/listings/999*' => Http::response([
|
|
'listing_id' => 999, 'shop_id' => 42, 'title' => 'Dragon figurine', 'state' => 'active',
|
|
])]);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$projectA = Project::factory()->create();
|
|
$projectB = Project::factory()->create();
|
|
ProjectListingLink::factory()->for($projectA)->create(['shop_id' => 42, 'listing_id' => 999, 'variation_key' => 'all']);
|
|
|
|
$this->actingAs($admin)->post(route('projects.listings.store', $projectB), ['listing_id' => 999])
|
|
->assertSessionHasErrors('listing_id');
|
|
}
|
|
|
|
public function test_reattaching_same_listing_to_same_project_does_not_duplicate(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$this->connectEtsy();
|
|
Http::fake(['https://api.etsy.com/v3/application/listings/999*' => Http::response([
|
|
'listing_id' => 999, 'shop_id' => 42, 'title' => 'Dragon figurine v2', 'state' => 'active',
|
|
])]);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$project = Project::factory()->create();
|
|
ProjectListingLink::factory()->for($project)->create(['shop_id' => 42, 'listing_id' => 999, 'variation_key' => 'all', 'title_snapshot' => 'old title']);
|
|
|
|
$this->actingAs($admin)->post(route('projects.listings.store', $project), ['listing_id' => 999])->assertRedirect();
|
|
|
|
$this->assertDatabaseCount('project_listing_links', 1);
|
|
$this->assertDatabaseHas('project_listing_links', ['listing_id' => 999, 'title_snapshot' => 'Dragon figurine v2']);
|
|
}
|
|
|
|
public function test_detaching_a_listing_preserves_its_release_history(): void
|
|
{
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$project = Project::factory()->create();
|
|
$link = ProjectListingLink::factory()->for($project)->create();
|
|
|
|
$this->actingAs($admin)->delete(route('projects.listings.destroy', [$project, $link]))->assertRedirect();
|
|
|
|
$link->refresh();
|
|
$this->assertNotNull($link->detached_at);
|
|
$this->assertModelExists($link);
|
|
}
|
|
|
|
public function test_attaching_a_new_unready_mapping_to_an_already_live_project_drops_it_back_to_design(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$this->connectEtsy();
|
|
Http::fake(['https://api.etsy.com/v3/application/listings/999*' => Http::response([
|
|
'listing_id' => 999, 'shop_id' => 42, 'title' => 'Dragon figurine', 'state' => 'active',
|
|
])]);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$project = Project::factory()->create(['stage' => 'live']);
|
|
$existingLink = ProjectListingLink::factory()->for($project)->create(['live_release_id' => null]);
|
|
$release = ProjectRelease::factory()->for($project)->create();
|
|
$existingLink->update(['live_release_id' => $release->id]);
|
|
|
|
$this->actingAs($admin)->post(route('projects.listings.store', $project), ['listing_id' => 999])
|
|
->assertRedirect();
|
|
|
|
$this->assertSame('design', $project->fresh()->stage);
|
|
$this->assertDatabaseHas('project_events', ['project_id' => $project->id, 'event_type' => 'stage_changed']);
|
|
}
|
|
|
|
public function test_rejects_an_arbitrary_variation_key_since_per_variant_mapping_is_not_implemented(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$this->connectEtsy();
|
|
Http::fake(['https://api.etsy.com/v3/application/listings/999*' => Http::response([
|
|
'listing_id' => 999, 'shop_id' => 42, 'title' => 'Dragon figurine', 'state' => 'active',
|
|
])]);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$project = Project::factory()->create();
|
|
|
|
$this->actingAs($admin)->post(route('projects.listings.store', $project), [
|
|
'listing_id' => 999, 'variation_key' => 'color:red',
|
|
])->assertSessionHasErrors('variation_key');
|
|
$this->assertDatabaseCount('project_listing_links', 0);
|
|
}
|
|
|
|
public function test_rejects_a_listing_that_is_inactive_rather_than_live_or_draft(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$this->connectEtsy();
|
|
Http::fake(['https://api.etsy.com/v3/application/listings/777*' => Http::response([
|
|
'listing_id' => 777, 'shop_id' => 42, 'title' => 'Old item', 'state' => 'inactive',
|
|
])]);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$project = Project::factory()->create();
|
|
|
|
$this->actingAs($admin)->post(route('projects.listings.store', $project), ['listing_id' => 777])
|
|
->assertSessionHasErrors('listing_id');
|
|
$this->assertDatabaseCount('project_listing_links', 0);
|
|
}
|
|
|
|
public function test_rejects_a_sold_out_listing(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$this->connectEtsy();
|
|
Http::fake(['https://api.etsy.com/v3/application/listings/778*' => Http::response([
|
|
'listing_id' => 778, 'shop_id' => 42, 'title' => 'Sold out item', 'state' => 'sold_out',
|
|
])]);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$project = Project::factory()->create();
|
|
|
|
$this->actingAs($admin)->post(route('projects.listings.store', $project), ['listing_id' => 778])
|
|
->assertSessionHasErrors('listing_id');
|
|
$this->assertDatabaseCount('project_listing_links', 0);
|
|
}
|
|
|
|
public function test_non_admin_cannot_attach_listings(): void
|
|
{
|
|
$project = Project::factory()->create();
|
|
|
|
$this->actingAs(User::factory()->create())->post(route('projects.listings.store', $project), ['listing_id' => 1])
|
|
->assertForbidden();
|
|
}
|
|
}
|