trypost/tests/Feature/Services/Social/TikTokCreatorInfoTest.php
Mohammed Almuhanna e52d1d7b25
fix(tiktok): send an empty JSON object to creator_info (#224)
Laravel serialises the empty array argument to `[]`. TikTok's
creator_info endpoint expects an object and rejects the array with
invalid_params, so every query failed and the service returned
emptyPayload(), leaving the composer with no creator data.

Co-authored-by: Paulo Castellano <paulo@castellanos.llc>
2026-08-05 18:39:32 -03:00

99 lines
3.5 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\SocialAccount;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Social\TikTokCreatorInfo;
use Illuminate\Support\Facades\Http;
beforeEach(function () {
$this->user = User::factory()->create();
$this->workspace = Workspace::factory()->create(['user_id' => $this->user->id]);
$this->account = SocialAccount::factory()->tiktok()->create([
'workspace_id' => $this->workspace->id,
'token_expires_at' => now()->addDays(1),
]);
$this->service = new TikTokCreatorInfo;
$this->api = config('trypost.platforms.tiktok.api');
});
test('it returns full creator payload from api response', function () {
Http::fake([
$this->api.'/post/publish/creator_info/query/' => Http::response([
'data' => [
'creator_nickname' => 'Paulo',
'creator_username' => 'paulocastellano',
'creator_avatar_url' => 'https://cdn.tiktok.com/avatar.jpg',
'privacy_level_options' => ['PUBLIC_TO_EVERYONE', 'MUTUAL_FOLLOW_FRIENDS', 'SELF_ONLY'],
'comment_disabled' => false,
'duet_disabled' => true,
'stitch_disabled' => true,
'max_video_post_duration_sec' => 600,
],
], 200),
]);
$info = $this->service->fetch($this->account);
expect($info['creator_nickname'])->toBe('Paulo')
->and($info['creator_username'])->toBe('paulocastellano')
->and($info['creator_avatar_url'])->toBe('https://cdn.tiktok.com/avatar.jpg')
->and($info['privacy_level_options'])->toBe(['PUBLIC_TO_EVERYONE', 'MUTUAL_FOLLOW_FRIENDS', 'SELF_ONLY'])
->and($info['comment_disabled'])->toBeFalse()
->and($info['duet_disabled'])->toBeTrue()
->and($info['stitch_disabled'])->toBeTrue()
->and($info['max_video_post_duration_sec'])->toBe(600);
});
test('it sends an empty json object as the request body', function () {
Http::fake([
$this->api.'/post/publish/creator_info/query/' => Http::response(['data' => []], 200),
]);
$this->service->fetch($this->account);
Http::assertSent(function ($request) {
return $request->body() === '{}';
});
});
test('it returns an empty payload when the api fails', function () {
Http::fake([
$this->api.'/post/publish/creator_info/query/' => Http::response(['error' => 'unauthorized'], 401),
]);
$info = $this->service->fetch($this->account);
expect($info['creator_nickname'])->toBeNull()
->and($info['privacy_level_options'])->toBe([])
->and($info['comment_disabled'])->toBeFalse()
->and($info['duet_disabled'])->toBeFalse()
->and($info['stitch_disabled'])->toBeFalse()
->and($info['max_video_post_duration_sec'])->toBeNull();
});
test('it refreshes the token before calling when expired', function () {
$this->account->update(['token_expires_at' => now()->subMinute()]);
Http::fake([
$this->api.'/oauth/token/' => Http::response([
'access_token' => 'new-token',
'refresh_token' => 'new-refresh',
'expires_in' => 3600,
], 200),
$this->api.'/post/publish/creator_info/query/' => Http::response([
'data' => [
'privacy_level_options' => ['PUBLIC_TO_EVERYONE'],
],
], 200),
]);
$this->service->fetch($this->account);
Http::assertSent(fn ($request) => str_contains($request->url(), '/oauth/token/'));
expect($this->account->fresh()->access_token)->toBe('new-token');
});