feat: ship creator journey v2 and profile updates
This commit is contained in:
@@ -3,9 +3,12 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Group;
|
||||
use App\Services\ArtworkService;
|
||||
use App\Services\HomepageService;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
beforeEach(function () {
|
||||
// Use null Scout driver — Meilisearch calls return empty results gracefully
|
||||
@@ -16,6 +19,9 @@ beforeEach(function () {
|
||||
$artworksMock->shouldReceive('getFeaturedArtworks')
|
||||
->andReturn(new LengthAwarePaginator(collect(), 0, 1))
|
||||
->byDefault();
|
||||
$artworksMock->shouldReceive('getFeaturedArtworkWinner')
|
||||
->andReturn(null)
|
||||
->byDefault();
|
||||
app()->instance(ArtworkService::class, $artworksMock);
|
||||
});
|
||||
|
||||
@@ -78,3 +84,34 @@ it('guest and auth homepages have different key sets', function () {
|
||||
expect(in_array('from_following', $auth))->toBeTrue();
|
||||
expect(in_array('from_following', $guest))->toBeFalse();
|
||||
});
|
||||
|
||||
it('homepage artwork payload uses group name and avatar for group-published artworks', function () {
|
||||
$owner = User::factory()->create();
|
||||
$group = Group::factory()->create([
|
||||
'owner_user_id' => $owner->id,
|
||||
'name' => 'Skinbase Collective',
|
||||
'slug' => 'skinbase-collective',
|
||||
]);
|
||||
|
||||
Artwork::factory()->create([
|
||||
'user_id' => $owner->id,
|
||||
'group_id' => $group->id,
|
||||
'published_as_type' => Artwork::PUBLISHED_AS_GROUP,
|
||||
'title' => 'Group Published Artwork',
|
||||
'hash' => 'homepagegroupartwork',
|
||||
'thumb_ext' => 'webp',
|
||||
'published_at' => now()->subMinute(),
|
||||
]);
|
||||
|
||||
Cache::flush();
|
||||
|
||||
$items = app(HomepageService::class)->getFreshUploads(10);
|
||||
|
||||
expect($items)->not->toBeEmpty()
|
||||
->and($items[0]['author'])->toBe('Skinbase Collective')
|
||||
->and($items[0]['author_username'])->toBe('')
|
||||
->and($items[0]['published_as_type'])->toBe(Artwork::PUBLISHED_AS_GROUP)
|
||||
->and($items[0]['publisher']['type'])->toBe('group')
|
||||
->and($items[0]['publisher']['name'])->toBe('Skinbase Collective')
|
||||
->and($items[0]['publisher']['profile_url'])->toContain('/groups/skinbase-collective');
|
||||
});
|
||||
|
||||
@@ -39,45 +39,52 @@ it('records a view and returns ok=true on first call', function () {
|
||||
DB::table('artwork_stats')->insertOrIgnore([
|
||||
'artwork_id' => $artwork->id,
|
||||
'views' => 0,
|
||||
'views_24h' => 0,
|
||||
'views_7d' => 0,
|
||||
'downloads' => 0,
|
||||
'favorites' => 0,
|
||||
'rating_avg' => 0,
|
||||
'rating_count' => 0,
|
||||
]);
|
||||
|
||||
$mock = $this->mock(ArtworkStatsService::class);
|
||||
$mock->shouldReceive('logViewEvent')
|
||||
->once()
|
||||
->with($artwork->id, null); // null = guest (unauthenticated request)
|
||||
$mock->shouldReceive('incrementViews')
|
||||
->once()
|
||||
->with($artwork->id, 1, true);
|
||||
|
||||
$response = $this->postJson("/api/art/{$artwork->id}/view");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('ok', true)
|
||||
->assertJsonPath('counted', true);
|
||||
|
||||
expect(DB::table('artwork_stats')->where('artwork_id', $artwork->id)->value('views'))->toBe(1);
|
||||
expect(DB::table('artwork_view_events')->where('artwork_id', $artwork->id)->count())->toBe(1);
|
||||
});
|
||||
|
||||
it('skips DB increment and returns counted=false if artwork was already viewed this session', function () {
|
||||
it('counts repeated visits for the same artwork', function () {
|
||||
$artwork = Artwork::factory()->create([
|
||||
'is_public' => true,
|
||||
'is_approved' => true,
|
||||
'published_at' => now()->subDay(),
|
||||
]);
|
||||
|
||||
// Mark as already viewed in the session
|
||||
session()->put("art_viewed.{$artwork->id}", true);
|
||||
DB::table('artwork_stats')->insertOrIgnore([
|
||||
'artwork_id' => $artwork->id,
|
||||
'views' => 0,
|
||||
'views_24h' => 0,
|
||||
'views_7d' => 0,
|
||||
'downloads' => 0,
|
||||
'favorites' => 0,
|
||||
'rating_avg' => 0,
|
||||
'rating_count' => 0,
|
||||
]);
|
||||
|
||||
$mock = $this->mock(ArtworkStatsService::class);
|
||||
$mock->shouldReceive('incrementViews')->never();
|
||||
$this->postJson("/api/art/{$artwork->id}/view")
|
||||
->assertStatus(200)
|
||||
->assertJsonPath('counted', true);
|
||||
|
||||
$response = $this->postJson("/api/art/{$artwork->id}/view");
|
||||
$this->postJson("/api/art/{$artwork->id}/view")
|
||||
->assertStatus(200)
|
||||
->assertJsonPath('counted', true);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('ok', true)
|
||||
->assertJsonPath('counted', false);
|
||||
expect(DB::table('artwork_stats')->where('artwork_id', $artwork->id)->value('views'))->toBe(2);
|
||||
expect(DB::table('artwork_view_events')->where('artwork_id', $artwork->id)->count())->toBe(2);
|
||||
});
|
||||
|
||||
// ── ArtworkDownloadController (POST /api/art/{id}/download) ──────────────────
|
||||
@@ -101,7 +108,7 @@ it('records a download and returns ok=true with a url', function () {
|
||||
$mock = $this->mock(ArtworkStatsService::class);
|
||||
$mock->shouldReceive('incrementDownloads')
|
||||
->once()
|
||||
->with($artwork->id, 1, true);
|
||||
->with($artwork->id, 1, false);
|
||||
|
||||
$response = $this->postJson("/api/art/{$artwork->id}/download");
|
||||
|
||||
@@ -120,7 +127,7 @@ it('inserts a row in artwork_downloads on valid download', function () {
|
||||
|
||||
// Stub the stats service so we don't need Redis
|
||||
$mock = $this->mock(ArtworkStatsService::class);
|
||||
$mock->shouldReceive('incrementDownloads')->once();
|
||||
$mock->shouldReceive('incrementDownloads')->once()->with($artwork->id, 1, false);
|
||||
|
||||
$this->actingAs($user)->postJson("/api/art/{$artwork->id}/download");
|
||||
|
||||
@@ -138,7 +145,7 @@ it('records download as guest (no user_id) when unauthenticated', function () {
|
||||
]);
|
||||
|
||||
$mock = $this->mock(ArtworkStatsService::class);
|
||||
$mock->shouldReceive('incrementDownloads')->once();
|
||||
$mock->shouldReceive('incrementDownloads')->once()->with($artwork->id, 1, false);
|
||||
|
||||
$this->postJson("/api/art/{$artwork->id}/download");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user