clever_tap_dubit 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.rubocop.yml +48 -0
- data/.travis.yml +6 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +164 -0
- data/Rakefile +6 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/clever_tap.gemspec +33 -0
- data/lib/clever_tap/client.rb +113 -0
- data/lib/clever_tap/config.rb +25 -0
- data/lib/clever_tap/entity.rb +87 -0
- data/lib/clever_tap/event.rb +30 -0
- data/lib/clever_tap/failed_response.rb +28 -0
- data/lib/clever_tap/profile.rb +7 -0
- data/lib/clever_tap/response.rb +34 -0
- data/lib/clever_tap/successful_response.rb +30 -0
- data/lib/clever_tap/uploader.rb +72 -0
- data/lib/clever_tap/version.rb +3 -0
- data/lib/clever_tap.rb +79 -0
- data/lib/clevertap-ruby.rb +1 -0
- data/spec/factories/profile.rb +36 -0
- data/spec/integrations/clever_tap_spec.rb +81 -0
- data/spec/rubocop_spec.rb +12 -0
- data/spec/shared/clever_tap_client.rb +13 -0
- data/spec/shared/entity.rb +105 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/units/clever_tap_client_spec.rb +277 -0
- data/spec/units/clever_tap_spec.rb +88 -0
- data/spec/units/event_spec.rb +43 -0
- data/spec/units/failed_response_spec.rb +31 -0
- data/spec/units/profile_spec.rb +29 -0
- data/spec/units/response_spec.rb +63 -0
- data/spec/units/successful_response_spec.rb +112 -0
- data/spec/units/uploader_spec.rb +129 -0
- data/spec/vcr_cassettes/CleverTap/uploading_a_many_profiles/when_only_some_are_valid/partially_succeds.yml +42 -0
- data/spec/vcr_cassettes/CleverTap/uploading_a_profile/when_is_invalid/fails.yml +41 -0
- data/spec/vcr_cassettes/CleverTap/uploading_a_profile/when_is_valid/succeed.yml +35 -0
- data/spec/vcr_cassettes/CleverTap/uploading_an_event/when_is_valid/succeed.yml +34 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_invalid_records/calls_on_failed_upload_once.yml +38 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_invalid_records/returns_an_array_with_one_failed_Response_object.yml +38 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_do_not_fit_upload_limit_/calls_on_successful_upload_proc_twice.yml +67 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_do_not_fit_upload_limit_/returns_an_array_with_two_successful_Response_objects.yml +67 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_fit_upload_limit_/calls_on_successful_upload_proc_once.yml +36 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_fit_upload_limit_/returns_an_array_with_one_successful_Response_object.yml +36 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_age_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_education_status_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +49 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_email_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_employment_status_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_marital_status_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_phone_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_the_creation_date_field_is_missing/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/with_invalid_credentials/failed_to_upload_the_profiles.yml +36 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/with_valid_data/makes_successful_upload.yml +36 -0
- data/spec/vcr_config.rb +13 -0
- metadata +192 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
class CleverTap
|
2
|
+
# Normalize the success response data to one interface with the failure one
|
3
|
+
class SuccessfulResponse
|
4
|
+
attr_reader :raw_response, :unprocessed, :message, :code
|
5
|
+
|
6
|
+
# NOTE: raw_response can include processed, unprocessed, status
|
7
|
+
def initialize(raw_response = {})
|
8
|
+
@raw_response = raw_response
|
9
|
+
@unprocessed = raw_response['unprocessed']
|
10
|
+
@message = ''
|
11
|
+
@code = 200
|
12
|
+
end
|
13
|
+
|
14
|
+
def errors
|
15
|
+
unprocessed
|
16
|
+
end
|
17
|
+
|
18
|
+
def status
|
19
|
+
case
|
20
|
+
when success then 'success'
|
21
|
+
when raw_response['processed'].positive? then 'partial'
|
22
|
+
else 'fail'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def success
|
27
|
+
unprocessed.empty?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class CleverTap
|
2
|
+
# unit uploading profile data to CleverTap
|
3
|
+
class Uploader
|
4
|
+
HTTP_PATH = 'upload'.freeze
|
5
|
+
|
6
|
+
TYPE_EVENT = :event
|
7
|
+
TYPE_PROFILE = :profile
|
8
|
+
|
9
|
+
ENTITY_DATA_NAMES = {
|
10
|
+
TYPE_EVENT => 'evtData',
|
11
|
+
TYPE_PROFILE => 'profileData'
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
attr_reader :records, :type, :identity_field, :date_field, :event_name, :dry_run
|
15
|
+
|
16
|
+
# TODO: make defaults configurable
|
17
|
+
# date_field should be a date object responding to `to_i` which
|
18
|
+
# should returns epoch time
|
19
|
+
# profile respond to .to_h
|
20
|
+
def initialize(records, identity_field:, date_field: nil, event_name: nil, dry_run: false)
|
21
|
+
@type = event_name ? TYPE_EVENT : TYPE_PROFILE
|
22
|
+
@records = records
|
23
|
+
|
24
|
+
@identity_field = identity_field
|
25
|
+
@date_field = date_field
|
26
|
+
@event_name = event_name
|
27
|
+
@dry_run = dry_run
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(client)
|
31
|
+
response = client.post(HTTP_PATH, build_request_body) do |request|
|
32
|
+
request.params.merge!(dryRun: 1) if dry_run
|
33
|
+
end
|
34
|
+
|
35
|
+
parse_response(response)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def build_request_body
|
41
|
+
records.each_with_object('d' => []) do |record, request_body|
|
42
|
+
request_body['d'] << normalize_record(record)
|
43
|
+
end.to_json
|
44
|
+
end
|
45
|
+
|
46
|
+
def normalize_record(record)
|
47
|
+
ts = date_field ? record[date_field] : Time.now
|
48
|
+
|
49
|
+
{
|
50
|
+
'identity' => pluck_identity(record).to_s,
|
51
|
+
'ts' => ts.to_i,
|
52
|
+
'type' => type,
|
53
|
+
ENTITY_DATA_NAMES[type] => record.to_h
|
54
|
+
}.tap do |hash|
|
55
|
+
hash.merge!('evtName' => event_name) if type == TYPE_EVENT
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_response(http_response)
|
60
|
+
http_response
|
61
|
+
end
|
62
|
+
|
63
|
+
def pluck_identity(record)
|
64
|
+
# TODO: symbolize record keys
|
65
|
+
identity = record[identity_field.to_s] || record[identity_field.to_sym]
|
66
|
+
|
67
|
+
raise "Missing identity field with name: '#{identity_field}' for #{record}" unless identity
|
68
|
+
|
69
|
+
identity
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/clever_tap.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require 'clever_tap/config'
|
4
|
+
require 'clever_tap/client'
|
5
|
+
require 'clever_tap/entity'
|
6
|
+
require 'clever_tap/event'
|
7
|
+
require 'clever_tap/profile'
|
8
|
+
require 'clever_tap/uploader'
|
9
|
+
require 'clever_tap/response'
|
10
|
+
require 'clever_tap/successful_response'
|
11
|
+
require 'clever_tap/failed_response'
|
12
|
+
|
13
|
+
# the main module of the system
|
14
|
+
class CleverTap
|
15
|
+
attr_reader :config
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# Never instantiated. Variables are stored in the singleton_class.
|
19
|
+
private_class_method :new
|
20
|
+
|
21
|
+
attr_accessor :identity_field
|
22
|
+
attr_accessor :account_id
|
23
|
+
attr_accessor :account_passcode
|
24
|
+
|
25
|
+
def setup
|
26
|
+
yield(self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(**params)
|
31
|
+
@config = Config.new(params)
|
32
|
+
yield(@config) if block_given?
|
33
|
+
|
34
|
+
@config.validate
|
35
|
+
@config.freeze
|
36
|
+
end
|
37
|
+
|
38
|
+
def client
|
39
|
+
@client ||= Client.new(config.account_id, config.passcode, &config.configure_faraday)
|
40
|
+
end
|
41
|
+
|
42
|
+
def upload_events(events, name:, **rest)
|
43
|
+
options = rest.merge(event_name: name, identity_field: config.identity_field)
|
44
|
+
|
45
|
+
response = Uploader.new(events, options).call(client)
|
46
|
+
|
47
|
+
normalize_response(response, records: events)
|
48
|
+
rescue Faraday::Error::TimeoutError, Faraday::Error::ClientError => e
|
49
|
+
FailedResponse.new(records: events, message: e.message)
|
50
|
+
end
|
51
|
+
|
52
|
+
def upload_event(event, **options)
|
53
|
+
upload_events([event], options)
|
54
|
+
end
|
55
|
+
|
56
|
+
def upload_profiles(profiles, **options)
|
57
|
+
options = options.merge(identity_field: config.identity_field)
|
58
|
+
response = Uploader.new(profiles, **options).call(client)
|
59
|
+
|
60
|
+
normalize_response(response, records: profiles)
|
61
|
+
rescue Faraday::Error::TimeoutError, Faraday::Error::ClientError => e
|
62
|
+
FailedResponse.new(records: profiles, message: e.message)
|
63
|
+
end
|
64
|
+
|
65
|
+
def upload_profile(profile, **options)
|
66
|
+
upload_profiles([profile], options)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def normalize_response(response, records:)
|
72
|
+
# TODO: handle JSON::ParserError
|
73
|
+
if response.success?
|
74
|
+
SuccessfulResponse.new(JSON.parse(response.body))
|
75
|
+
else
|
76
|
+
FailedResponse.new(records: records, code: response.status, message: response.body)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'clever_tap'
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Unit representing a profile and it's different states
|
2
|
+
class Profile
|
3
|
+
class << self
|
4
|
+
attr_accessor :store
|
5
|
+
end
|
6
|
+
|
7
|
+
self.store = {
|
8
|
+
id: 0
|
9
|
+
}
|
10
|
+
|
11
|
+
def self.build_valid(extra = {})
|
12
|
+
new({
|
13
|
+
'identity' => store[:id] += 1,
|
14
|
+
'created_at' => Time.new,
|
15
|
+
'Name' => 'John Rush',
|
16
|
+
'Email' => 'example@gmail.com',
|
17
|
+
'Gender' => 'M',
|
18
|
+
'Phone' => '+35922333232',
|
19
|
+
'Employed' => 'Y',
|
20
|
+
'Education' => 'Graduate',
|
21
|
+
'Married' => 'Y',
|
22
|
+
'Age' => '18'
|
23
|
+
}.merge(extra))
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :data
|
27
|
+
alias to_h data
|
28
|
+
|
29
|
+
def initialize(data = {})
|
30
|
+
@data = data
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](key)
|
34
|
+
data[key]
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CleverTap, vcr: true do
|
4
|
+
# NOTE: clear mutations in CleverTap config
|
5
|
+
subject(:clever_tap) { CleverTap.new(account_id: AUTH_ACCOUNT_ID, passcode: AUTH_PASSCODE) }
|
6
|
+
|
7
|
+
describe 'uploading a profile' do
|
8
|
+
context 'when is valid' do
|
9
|
+
let(:profile) { Profile.build_valid }
|
10
|
+
|
11
|
+
it 'succeed' do
|
12
|
+
response = clever_tap.upload_profile(profile)
|
13
|
+
|
14
|
+
aggregate_failures do
|
15
|
+
expect(response.status).to eq('success')
|
16
|
+
expect(response.errors).to be_empty
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when is invalid' do
|
22
|
+
let(:profile) { Profile.build_valid('Email' => '$$$$$') }
|
23
|
+
|
24
|
+
it 'fails' do
|
25
|
+
response = clever_tap.upload_profile(profile)
|
26
|
+
|
27
|
+
aggregate_failures do
|
28
|
+
expect(response.status).to eq('fail')
|
29
|
+
expect(response.errors).to all(be_a(Hash))
|
30
|
+
expect(response.errors).to all(
|
31
|
+
include('status', 'code', 'error', 'record')
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'uploading a many profiles' do
|
39
|
+
context 'when only some are valid' do
|
40
|
+
let(:valid_profile) { Profile.build_valid }
|
41
|
+
let(:invalid_profile) { Profile.build_valid('Email' => '$$$$$') }
|
42
|
+
let(:profiles) { [valid_profile, invalid_profile] }
|
43
|
+
|
44
|
+
it 'partially succeds' do
|
45
|
+
response = clever_tap.upload_profiles(profiles)
|
46
|
+
|
47
|
+
aggregate_failures do
|
48
|
+
expect(response.status).to eq('partial')
|
49
|
+
expect(response.errors).to all(be_a(Hash))
|
50
|
+
expect(response.errors).to all(
|
51
|
+
include('status', 'code', 'error', 'record')
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'uploading an event' do
|
59
|
+
subject(:clever_tap) do
|
60
|
+
CleverTap.new(account_id: AUTH_ACCOUNT_ID, passcode: AUTH_PASSCODE, identity_field: 'ID')
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when is valid' do
|
64
|
+
let(:event) do
|
65
|
+
{
|
66
|
+
'ID' => 555,
|
67
|
+
'mobile' => true
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'succeed' do
|
72
|
+
response = clever_tap.upload_event(event, name: 'register', identity_field: 'user_id')
|
73
|
+
|
74
|
+
aggregate_failures do
|
75
|
+
expect(response.status).to eq('success')
|
76
|
+
expect(response.errors).to be_empty
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubocop'
|
2
|
+
|
3
|
+
gem_root = File.expand_path('../', __FILE__)
|
4
|
+
CONFIG_FILE_PATH = File.join(gem_root, 'config', 'rubocop_spec.yml')
|
5
|
+
|
6
|
+
describe 'rubocop' do
|
7
|
+
let(:args) { ['--format', 'simple', '-D', gem_root] }
|
8
|
+
|
9
|
+
it 'passes all cops' do
|
10
|
+
expect { RuboCop::CLI.new.run(args) }.to output(/no offenses detected/).to_stdout
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
shared_examples 'configured `Client`' do
|
2
|
+
it 'preserves credentials in `Client`' do
|
3
|
+
expect(subject.account_id).to eq account_id
|
4
|
+
expect(subject.passcode).to eq account_passcode
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
shared_examples 'configured `Client`' do
|
9
|
+
it 'preserves credentials in `Client`' do
|
10
|
+
expect(subject.account_id).to eq account_id
|
11
|
+
expect(subject.passcode).to eq account_passcode
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
shared_examples_for 'setting allowed identities for' do |type|
|
2
|
+
type_data = type == 'event' ? 'evtData' : 'profileData'
|
3
|
+
described_class::ALLOWED_IDENTITIES.each do |id|
|
4
|
+
context "when `identity` set as `#{id}` in the event" do
|
5
|
+
let!(:params_ext) { params.merge!(identity: id) }
|
6
|
+
let!(:data_ext) { data.merge!(id => '1414') }
|
7
|
+
|
8
|
+
it { is_expected.to include(id => '1414') }
|
9
|
+
it { expect(subject[type_data]).not_to include(id => '1414') }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
shared_examples_for 'choosing identity for' do |type|
|
15
|
+
evt_name = type == 'event' ? { name: 'Evt' } : {}
|
16
|
+
before { CleverTap.setup { |c| c.identity_field = 'ID' } }
|
17
|
+
let(:params) { { data: data }.merge!(evt_name) }
|
18
|
+
let(:data) { { 'ID' => 1, 'Name' => 'John' } }
|
19
|
+
|
20
|
+
context 'when custom `identity` from config' do
|
21
|
+
it { is_expected.to include 'identity' => '1' }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when `identity` different from ALLOWED_IDENTITIES and config' do
|
25
|
+
let!(:params_ext) { params.merge!(identity: 'email') }
|
26
|
+
let!(:data_ext) { data.merge!('email' => 'example@email.com') }
|
27
|
+
|
28
|
+
it { is_expected.to include 'identity' => '1' }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when `identity` missing from `data`' do
|
32
|
+
let(:data) { { 'Name' => 'John' } }
|
33
|
+
|
34
|
+
it { expect { subject }.to raise_error CleverTap::MissingIdentityError }
|
35
|
+
end
|
36
|
+
|
37
|
+
it_behaves_like 'setting allowed identities for', type
|
38
|
+
end
|
39
|
+
|
40
|
+
shared_examples_for 'choosing timestamp' do
|
41
|
+
let(:data) { { 'FBID' => 1, 'Name' => 'John' } }
|
42
|
+
let(:params) { { data: data, identity: 'FBID', name: 'evt' } }
|
43
|
+
|
44
|
+
context 'when no `timestamp_field`' do
|
45
|
+
it { is_expected.not_to include 'ts' }
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when specific `timestamp` field' do
|
49
|
+
let!(:data_ext) { data.merge!('Open Time' => open_time) }
|
50
|
+
let!(:params_ext) { params.merge!(timestamp_field: 'Open Time') }
|
51
|
+
|
52
|
+
context 'and `timestamp_field` is Unix timestamp' do
|
53
|
+
let(:open_time) { '1508241881' }
|
54
|
+
it { is_expected.to include('ts' => open_time.to_i) }
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'and `timestamp_field` is `DateTime` timestamp' do
|
58
|
+
let(:open_time) { Time.now }
|
59
|
+
it { is_expected.to include('ts' => open_time.to_i) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when `custom_timestamp` specified' do
|
64
|
+
let!(:params_ext) { params.merge!(custom_timestamp: open_time) }
|
65
|
+
|
66
|
+
context 'and `custom_timestamp` is Unix timestamp' do
|
67
|
+
let(:open_time) { '1508241881' }
|
68
|
+
it { is_expected.to include('ts' => open_time.to_i) }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'and `custom_timestamp` is `DateTime` timestamp' do
|
72
|
+
let(:open_time) { Time.now }
|
73
|
+
it { is_expected.to include('ts' => open_time.to_i) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
shared_examples_for 'proper type' do
|
79
|
+
let(:data) { { 'FBID' => '1414', 'Name' => 'John' } }
|
80
|
+
let(:params) { { data: data, name: 'e', identity: 'FBID' } }
|
81
|
+
|
82
|
+
it { is_expected.to include described_class::TYPE_KEY_STRING => described_class::TYPE_VALUE_STRING }
|
83
|
+
end
|
84
|
+
|
85
|
+
shared_examples_for 'constructing data for' do |type|
|
86
|
+
obj_type = type == 'event' ? 'evtData' : 'profileData'
|
87
|
+
evt_name = type == 'event' ? { name: 'Evt' } : {}
|
88
|
+
|
89
|
+
let(:data) { { 'FBID' => '1414', 'Name' => 'John' } }
|
90
|
+
let(:params) { { data: data, identity: 'FBID' }.merge!(evt_name) }
|
91
|
+
|
92
|
+
context 'when no `data` param in `params` hash' do
|
93
|
+
let(:params) { {}.merge!(evt_name) }
|
94
|
+
it { expect { subject }.to raise_error CleverTap::NoDataError }
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'when `data` empty hash in `params` hash' do
|
98
|
+
let(:params) { { data: {} }.merge!(evt_name) }
|
99
|
+
it { expect { subject }.to raise_error CleverTap::NoDataError }
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'when `data` available in `params` hash' do
|
103
|
+
it { is_expected.to include(obj_type => { 'Name' => 'John' }) }
|
104
|
+
end
|
105
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'clever_tap'
|
4
|
+
|
5
|
+
require 'vcr'
|
6
|
+
require 'vcr_config'
|
7
|
+
|
8
|
+
require 'factories/profile'
|
9
|
+
require 'pry-byebug'
|
10
|
+
|
11
|
+
# Use for recording VCR cassettes
|
12
|
+
AUTH_ACCOUNT_ID = ENV['CLEVER_TAP_ACCOUNT_ID'] || 'fake-id'
|
13
|
+
AUTH_PASSCODE = ENV['CLEVER_TAP_PASSCODE'] || 'fake-passcode'
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
17
|
+
config.mock_with(:rspec) { |c| c.syntax = :expect }
|
18
|
+
end
|