Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'TikTok Callbacks', type: :request do
|
||||
let(:account) { create(:account) }
|
||||
|
||||
let(:client_secret) { 'tiktok-app-secret' }
|
||||
let(:client_id) { 'tiktok-app-id' }
|
||||
|
||||
let(:token_endpoint) { 'https://business-api.tiktok.com/open_api/v1.3/tt_user/oauth2/token/' }
|
||||
let(:business_endpoint) { 'https://business-api.tiktok.com/open_api/v1.3/business/get/' }
|
||||
|
||||
let(:tiktok_access_token) { 'access-token-1' }
|
||||
let(:tiktok_refresh_token) { 'refresh-token-1' }
|
||||
let(:tiktok_business_id) { 'biz-123' }
|
||||
|
||||
let(:token_response) do
|
||||
{
|
||||
code: 0,
|
||||
message: 'ok',
|
||||
data: {
|
||||
open_id: tiktok_business_id,
|
||||
scope: Tiktok::AuthClient::REQUIRED_SCOPES.join(','),
|
||||
access_token: tiktok_access_token,
|
||||
refresh_token: tiktok_refresh_token,
|
||||
expires_in: 86_400,
|
||||
refresh_token_expires_in: 2_592_000
|
||||
}
|
||||
}.to_json
|
||||
end
|
||||
|
||||
let(:business_response) do
|
||||
{
|
||||
code: 0,
|
||||
message: 'ok',
|
||||
data: {
|
||||
username: 'tiktok_user',
|
||||
display_name: 'TikTok Display Name',
|
||||
profile_image: 'https://www.example.com/avatar.png'
|
||||
}
|
||||
}.to_json
|
||||
end
|
||||
|
||||
let(:state) do
|
||||
JWT.encode({ sub: account.id, iat: Time.current.to_i }, client_secret, 'HS256')
|
||||
end
|
||||
|
||||
before do
|
||||
InstallationConfig.where(name: %w[TIKTOK_APP_ID TIKTOK_APP_SECRET]).delete_all
|
||||
GlobalConfig.clear_cache
|
||||
|
||||
stub_request(:post, token_endpoint).to_return(status: 200, body: token_response, headers: { 'Content-Type' => 'application/json' })
|
||||
stub_request(:get, business_endpoint)
|
||||
.with(query: hash_including('business_id' => tiktok_business_id))
|
||||
.to_return(status: 200, body: business_response, headers: { 'Content-Type' => 'application/json' })
|
||||
end
|
||||
|
||||
it 'creates channel and inbox and redirects to agents step for new connections' do
|
||||
expect(Avatar::AvatarFromUrlJob).to receive(:perform_later).with(instance_of(Inbox), 'https://www.example.com/avatar.png')
|
||||
|
||||
with_modified_env TIKTOK_APP_ID: client_id, TIKTOK_APP_SECRET: client_secret do
|
||||
expect do
|
||||
get '/tiktok/callback', params: { code: 'valid_code', state: state }
|
||||
end.to change(Channel::Tiktok, :count).by(1).and change(Inbox, :count).by(1)
|
||||
end
|
||||
|
||||
inbox = Inbox.last
|
||||
channel = inbox.channel
|
||||
|
||||
expect(channel.business_id).to eq(tiktok_business_id)
|
||||
expect(channel.access_token).to eq(tiktok_access_token)
|
||||
expect(channel.refresh_token).to eq(tiktok_refresh_token)
|
||||
|
||||
expect(response).to redirect_to(app_tiktok_inbox_agents_url(account_id: account.id, inbox_id: inbox.id))
|
||||
end
|
||||
|
||||
it 'updates an existing channel and redirects to settings' do
|
||||
existing_channel = create(
|
||||
:channel_tiktok,
|
||||
account: account,
|
||||
business_id: tiktok_business_id,
|
||||
access_token: 'old-access-token',
|
||||
refresh_token: 'old-refresh-token',
|
||||
expires_at: 1.hour.ago,
|
||||
refresh_token_expires_at: 1.day.from_now
|
||||
)
|
||||
existing_channel.inbox.update!(name: 'Old Name')
|
||||
|
||||
with_modified_env TIKTOK_APP_ID: client_id, TIKTOK_APP_SECRET: client_secret do
|
||||
expect do
|
||||
get '/tiktok/callback', params: { code: 'valid_code', state: state }
|
||||
end.to not_change(Channel::Tiktok, :count).and not_change(Inbox, :count)
|
||||
end
|
||||
|
||||
existing_channel.reload
|
||||
inbox = existing_channel.inbox.reload
|
||||
|
||||
expect(existing_channel.access_token).to eq(tiktok_access_token)
|
||||
expect(existing_channel.refresh_token).to eq(tiktok_refresh_token)
|
||||
expect(inbox.name).to eq('TikTok Display Name')
|
||||
|
||||
expect(response).to redirect_to(app_tiktok_inbox_settings_url(account_id: account.id, inbox_id: inbox.id))
|
||||
end
|
||||
|
||||
it 'redirects to error page when user denies authorization' do
|
||||
with_modified_env TIKTOK_APP_ID: client_id, TIKTOK_APP_SECRET: client_secret do
|
||||
get '/tiktok/callback', params: { error: 'access_denied', error_description: 'User cancelled', error_code: '400', state: state }
|
||||
end
|
||||
|
||||
expect(response).to redirect_to(
|
||||
app_new_tiktok_inbox_url(
|
||||
account_id: account.id,
|
||||
error_type: 'access_denied',
|
||||
code: '400',
|
||||
error_message: 'User cancelled'
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
it 'redirects to error page when required scopes are not granted' do
|
||||
stub_request(:post, token_endpoint).to_return(
|
||||
status: 200,
|
||||
body: JSON.parse(token_response).deep_merge('data' => { 'scope' => 'user.info.basic' }).to_json,
|
||||
headers: { 'Content-Type' => 'application/json' }
|
||||
)
|
||||
|
||||
with_modified_env TIKTOK_APP_ID: client_id, TIKTOK_APP_SECRET: client_secret do
|
||||
get '/tiktok/callback', params: { code: 'valid_code', state: state }
|
||||
end
|
||||
|
||||
expect(response).to redirect_to(
|
||||
app_new_tiktok_inbox_url(
|
||||
account_id: account.id,
|
||||
error_type: 'ungranted_scopes',
|
||||
code: 400,
|
||||
error_message: 'User did not grant all the required scopes'
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user