Implement MakerWorld collections sync every 10 minutes and Artikel Beheer staging workflow

This commit is contained in:
2026-11-01 15:39:10 +01:00
parent 121ff624de
commit 2d78d80934
20 changed files with 1984 additions and 675 deletions
-1
View File
@@ -216,4 +216,3 @@ class AuthAndEmailTest extends TestCase
});
}
}
@@ -0,0 +1,290 @@
<?php
namespace Tests\Feature;
use App\Models\Category;
use App\Models\MakerWorldCollection;
use App\Models\Product;
use App\Models\User;
use App\Services\MakerWorldService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;
class MakerWorldCollectionSyncTest extends TestCase
{
use RefreshDatabase;
protected User $admin;
protected Category $category;
protected function setUp(): void
{
parent::setUp();
$this->admin = User::create([
'name' => 'Admin Jouwdriedee',
'email' => 'info@jouwdriedee.nl',
'password' => bcrypt('Jw3D!x9#QmK7$vR2'),
'is_admin' => true,
]);
$this->category = Category::create([
'name' => 'Fidget Toys & Gadgets',
'slug' => 'fidget-toys',
'description' => 'Leuke fidget toys',
'is_featured' => true,
]);
$this->actingAs($this->admin);
}
protected function fakeCollectionHttp(): void
{
Http::fake([
'https://api.bambulab.com/v1/design-service/favorites/9205051' => Http::response([
'id' => 9205051,
'title' => 'Top Populaire Modellen',
'description' => 'Mijn favoriete collectie',
'cover' => 'https://makerworld.bblmw.com/collection_cover.jpg',
'designCnt' => 2,
'designs' => [
[
'id' => 1740435,
'title' => 'Glow in the dark Fidget Slug',
'cover' => 'https://makerworld.bblmw.com/slug_cover.jpg',
'summary' => '<p>Super flexibele fidget slug.</p>',
'designCreator' => [
'name' => 'MakerBob',
'handle' => 'maker_bob',
],
'instances' => [
[
'title' => '0.4mm High Speed Profile',
'printerName' => 'Bambu Lab P1S',
'nozzleDiameter' => '0.4 mm',
'weight' => 75.5,
'prediction' => 7200, // 2 hours
'filamentConfig' => ['PLA Silk Rainbow'],
],
],
'designExtension' => [
'design_pictures' => [
['url' => 'https://makerworld.bblmw.com/slug_photo_1.jpg'],
['url' => 'https://makerworld.bblmw.com/slug_photo_2.jpg'],
],
'real_pictures' => [
['url' => 'https://makerworld.bblmw.com/slug_real_1.jpg'],
],
],
],
],
], 200),
'https://api.bambulab.com/v1/design-service/design/1740435' => Http::response([
'id' => 1740435,
'title' => 'Glow in the dark Fidget Slug',
'coverUrl' => 'https://makerworld.bblmw.com/slug_cover.jpg',
'summary' => '<p>Super flexibele fidget slug.</p>',
'license' => 'CC-BY-SA',
'designCreator' => [
'name' => 'MakerBob',
'handle' => 'maker_bob',
],
'instances' => [
[
'title' => '0.4mm High Speed Profile',
'printerName' => 'Bambu Lab P1S',
'nozzleDiameter' => '0.4 mm',
'weight' => 75.5,
'prediction' => 7200,
],
],
'designExtension' => [
'design_pictures' => [
['url' => 'https://makerworld.bblmw.com/slug_photo_1.jpg'],
['url' => 'https://makerworld.bblmw.com/slug_photo_2.jpg'],
],
],
], 200),
]);
}
public function test_admin_can_add_makerworld_collection(): void
{
$this->fakeCollectionHttp();
$response = $this->post(route('admin.makerworld.collections.add'), [
'collection_input' => 'https://makerworld.com/nl/collections/9205051',
'is_active_sync' => true,
'sync_now' => true,
]);
$response->assertRedirect(route('admin.makerworld.index', ['tab' => 'collections']));
$response->assertSessionHas('success');
$this->assertDatabaseHas('makerworld_collections', [
'makerworld_collection_id' => '9205051',
'name' => 'Top Populaire Modellen',
'is_active_sync' => true,
]);
}
public function test_collection_sync_creates_draft_products_with_photos_and_print_info(): void
{
$this->fakeCollectionHttp();
$collection = MakerWorldCollection::create([
'makerworld_collection_id' => '9205051',
'name' => 'Top Populaire Modellen',
'is_active_sync' => true,
'sync_interval_minutes' => 10,
]);
/** @var MakerWorldService $service */
$service = app(MakerWorldService::class);
$result = $service->syncCollection($collection);
$this->assertEquals(1, $result['imported']);
$this->assertNull($result['error']);
// Assert database record
$product = Product::where('makerworld_id', 1740435)->first();
$this->assertNotNull($product);
$this->assertEquals('draft', $product->status);
$this->assertFalse($product->is_active);
$this->assertEquals($collection->id, $product->makerworld_collection_id);
$this->assertEquals('https://makerworld.bblmw.com/slug_cover.jpg', $product->image_url);
$this->assertNotEmpty($product->gallery_images);
$this->assertEquals(76, $product->weight_grams);
$this->assertEquals(2.0, $product->print_time_hours);
$this->assertNotNull($product->print_info);
$this->assertEquals('0.4 mm', $product->print_info['nozzle']);
// Assert product is NOT visible on customer storefront
$shopResponse = $this->get(route('catalog'));
$shopResponse->assertOk();
$shopResponse->assertDontSee('Glow in the dark Fidget Slug');
}
public function test_artisan_sync_makerworld_collections_command(): void
{
$this->fakeCollectionHttp();
$collection = MakerWorldCollection::create([
'makerworld_collection_id' => '9205051',
'name' => 'Top Populaire Modellen',
'is_active_sync' => true,
'sync_interval_minutes' => 10,
]);
$exitCode = Artisan::call('app:sync-makerworld-collections');
$this->assertEquals(0, $exitCode);
$this->assertDatabaseHas('products', [
'makerworld_id' => 1740435,
'status' => 'draft',
'makerworld_collection_id' => $collection->id,
]);
}
public function test_admin_can_publish_staged_product_in_artikel_beheer(): void
{
$product = Product::create([
'makerworld_id' => 1740435,
'name' => 'Staged Fidget Model',
'slug' => 'staged-fidget-model',
'short_description' => 'Korte omschrijving',
'description' => 'Volledige omschrijving',
'base_price' => 12.50,
'status' => 'draft',
'is_active' => false,
'category_id' => $this->category->id,
]);
$response = $this->post(route('admin.makerworld.staging.publish', $product), [
'name' => 'Gecureerde Fidget Slug Deluxe',
'category_id' => $this->category->id,
'base_price' => 19.95,
'is_featured' => true,
]);
$response->assertRedirect();
$response->assertSessionHas('success');
$product->refresh();
$this->assertEquals('published', $product->status);
$this->assertTrue($product->is_active);
$this->assertEquals(19.95, $product->base_price);
$this->assertEquals('Gecureerde Fidget Slug Deluxe', $product->name);
// Verify it is now visible in the customer catalog
$catalogResp = $this->get(route('catalog'));
$catalogResp->assertOk();
$catalogResp->assertSee('Gecureerde Fidget Slug Deluxe');
}
public function test_admin_can_bulk_publish_staged_products(): void
{
$p1 = Product::create([
'makerworld_id' => 101,
'name' => 'Concept 1',
'slug' => 'concept-1',
'description' => 'Test 1',
'base_price' => 10.00,
'status' => 'draft',
'is_active' => false,
]);
$p2 = Product::create([
'makerworld_id' => 102,
'name' => 'Concept 2',
'slug' => 'concept-2',
'description' => 'Test 2',
'base_price' => 15.00,
'status' => 'draft',
'is_active' => false,
]);
$response = $this->post(route('admin.makerworld.staging.bulk-publish'), [
'product_ids' => [$p1->id, $p2->id],
'category_id' => $this->category->id,
'custom_markup' => 20, // 20% markup
]);
$response->assertRedirect();
$response->assertSessionHas('success');
$p1->refresh();
$p2->refresh();
$this->assertEquals('published', $p1->status);
$this->assertTrue($p1->is_active);
$this->assertEquals(12.00, $p1->base_price); // 10 + 20%
$this->assertEquals('published', $p2->status);
$this->assertTrue($p2->is_active);
$this->assertEquals(18.00, $p2->base_price); // 15 + 20%
}
public function test_admin_can_toggle_sync_and_delete_collection(): void
{
$collection = MakerWorldCollection::create([
'makerworld_collection_id' => '8888',
'name' => 'Test Collection',
'is_active_sync' => true,
]);
// Toggle sync
$toggleResp = $this->post(route('admin.makerworld.collections.toggle', $collection));
$toggleResp->assertRedirect();
$collection->refresh();
$this->assertFalse($collection->is_active_sync);
// Delete collection
$deleteResp = $this->delete(route('admin.makerworld.collections.delete', $collection));
$deleteResp->assertRedirect();
$this->assertDatabaseMissing('makerworld_collections', ['id' => $collection->id]);
}
}