Skip to content

Commit 2b547da

Browse files
committed
test(analytics): add integration tests for new analytics API
1 parent 9bb56f6 commit 2b547da

File tree

3 files changed

+394
-53
lines changed

3 files changed

+394
-53
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# frozen_string_literal: true
2+
3+
require_relative '../spec_helper'
4+
require_relative 'shared_configuration_context'
5+
6+
describe Typesense::AnalyticsEvents do
7+
subject(:analytics_events) { typesense.analytics.events }
8+
9+
include_context 'with Typesense configuration'
10+
11+
let(:rule_name) { 'test__rule' }
12+
let(:rule_configuration) do
13+
{
14+
'name' => rule_name,
15+
'type' => 'counter',
16+
'collection' => 'test_products',
17+
'event_type' => 'click',
18+
'rule_tag' => 'test_tag',
19+
'params' => {
20+
'counter_field' => 'popularity',
21+
'weight' => 1
22+
}
23+
}
24+
end
25+
26+
let(:integration_client) do
27+
Typesense::Client.new(
28+
nodes: [{
29+
host: 'localhost',
30+
port: '8108',
31+
protocol: 'http'
32+
}],
33+
api_key: 'xyz',
34+
connection_timeout_seconds: 10
35+
)
36+
end
37+
38+
before do
39+
skip('New Analytics API is not supported in Typesense 29.0 and below') unless typesense_v30_or_above?
40+
41+
WebMock.disable!
42+
43+
begin
44+
integration_client.collections.create({
45+
'name' => 'test_products',
46+
'fields' => [
47+
{ 'name' => 'company_name', 'type' => 'string' },
48+
{ 'name' => 'num_employees', 'type' => 'int32' },
49+
{ 'name' => 'country', 'type' => 'string', 'facet' => true },
50+
{ 'name' => 'popularity', 'type' => 'int32', 'optional' => true }
51+
],
52+
'default_sorting_field' => 'num_employees'
53+
})
54+
rescue Typesense::Error::ObjectAlreadyExists
55+
# Collection already exists, which is fine
56+
end
57+
58+
begin
59+
integration_client.analytics.rules.create([rule_configuration])
60+
rescue StandardError
61+
# Rule creation might fail, which is fine for testing
62+
end
63+
end
64+
65+
after do
66+
begin
67+
rules = integration_client.analytics.rules.retrieve
68+
if rules.is_a?(Array)
69+
rules.each do |rule|
70+
next unless rule['name'].to_s.start_with?('test__')
71+
72+
begin
73+
integration_client.analytics.rules[rule['name']].delete
74+
rescue StandardError
75+
# Ignore cleanup errors
76+
end
77+
end
78+
end
79+
rescue StandardError
80+
# Ignore cleanup errors
81+
end
82+
83+
begin
84+
integration_client.collections['test_products'].delete
85+
rescue StandardError
86+
# Ignore cleanup errors
87+
end
88+
89+
WebMock.enable!
90+
end
91+
92+
describe '#create' do
93+
it 'creates an analytics event and returns it' do
94+
event = {
95+
'name' => rule_name,
96+
'event_type' => 'click',
97+
'data' => {
98+
'doc_id' => '1',
99+
'user_id' => 'test_user'
100+
}
101+
}
102+
103+
result = integration_client.analytics.events.create(event)
104+
expect(result).to be_a(Hash)
105+
end
106+
end
107+
108+
describe '#retrieve' do
109+
it 'retrieves analytics events with query parameters' do
110+
event = {
111+
'name' => rule_name,
112+
'event_type' => 'click',
113+
'data' => {
114+
'doc_id' => '1',
115+
'user_id' => 'test_user'
116+
}
117+
}
118+
119+
integration_client.analytics.events.create(event)
120+
121+
result = integration_client.analytics.events.retrieve({
122+
'user_id' => 'test_user',
123+
'name' => rule_name,
124+
'n' => 10
125+
})
126+
127+
expect(result).to be_a(Hash)
128+
expect(result['events']).to be_a(Array)
129+
end
130+
end
131+
132+
describe 'event creation with different event types' do
133+
it 'creates click and conversion events' do
134+
click_event = {
135+
'name' => rule_name,
136+
'event_type' => 'click',
137+
'data' => {
138+
'doc_id' => '1',
139+
'user_id' => 'test_user'
140+
}
141+
}
142+
143+
conversion_event = {
144+
'name' => rule_name,
145+
'event_type' => 'conversion',
146+
'data' => {
147+
'doc_id' => '1',
148+
'user_id' => 'test_user'
149+
}
150+
}
151+
152+
click_response = integration_client.analytics.events.create(click_event)
153+
expect(click_response).to be_a(Hash)
154+
155+
conversion_response = integration_client.analytics.events.create(conversion_event)
156+
expect(conversion_response).to be_a(Hash)
157+
end
158+
end
159+
end

spec/typesense/analytics_rule_spec.rb

Lines changed: 107 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,135 @@
44
require_relative 'shared_configuration_context'
55

66
describe Typesense::AnalyticsRule do
7-
subject(:analytics_rule) { typesense.analytics.rules['search_suggestions'] }
7+
subject(:analytics_rule) { typesense.analytics.rules[rule_name] }
88

99
include_context 'with Typesense configuration'
1010

11-
let(:analytics_rule_data) do
11+
let(:rule_name) { 'test__rule' }
12+
let(:rule_configuration) do
1213
{
13-
'name' => 'search_suggestions',
14-
'type' => 'popular_queries',
14+
'name' => rule_name,
15+
'type' => 'counter',
16+
'collection' => 'test_products',
17+
'event_type' => 'click',
18+
'rule_tag' => 'test_tag',
1519
'params' => {
16-
'source' => { 'collections' => ['products'] },
17-
'destination' => { 'collection' => 'products_top_queries' },
18-
'limit' => 100
20+
'counter_field' => 'popularity',
21+
'weight' => 1
1922
}
2023
}
2124
end
2225

26+
let(:integration_client) do
27+
Typesense::Client.new(
28+
nodes: [{
29+
host: 'localhost',
30+
port: '8108',
31+
protocol: 'http'
32+
}],
33+
api_key: 'xyz',
34+
connection_timeout_seconds: 10
35+
)
36+
end
37+
2338
before do
24-
skip('Analytics is deprecated in Typesense v30+') if typesense_v30_or_above?
39+
skip('New Analytics API is not supported in Typesense 29.0 and below') unless typesense_v30_or_above?
40+
41+
WebMock.disable!
42+
43+
# Create test collection
44+
begin
45+
integration_client.collections.create({
46+
'name' => 'test_products',
47+
'fields' => [
48+
{ 'name' => 'company_name', 'type' => 'string' },
49+
{ 'name' => 'num_employees', 'type' => 'int32' },
50+
{ 'name' => 'country', 'type' => 'string', 'facet' => true },
51+
{ 'name' => 'popularity', 'type' => 'int32', 'optional' => true }
52+
],
53+
'default_sorting_field' => 'num_employees'
54+
})
55+
rescue Typesense::Error::ObjectAlreadyExists
56+
# Collection already exists, which is fine
57+
end
58+
59+
# Create test rule
60+
begin
61+
integration_client.analytics.rules.create([rule_configuration])
62+
rescue StandardError
63+
# Rule creation might fail, which is fine for testing
64+
end
65+
end
66+
67+
after do
68+
# Clean up test rules
69+
begin
70+
rules = integration_client.analytics.rules.retrieve
71+
if rules.is_a?(Array)
72+
rules.each do |rule|
73+
next unless rule['name'].to_s.start_with?('test__')
74+
75+
begin
76+
integration_client.analytics.rules[rule['name']].delete
77+
rescue StandardError
78+
# Ignore cleanup errors
79+
end
80+
end
81+
end
82+
rescue StandardError
83+
# Ignore cleanup errors
84+
end
85+
86+
# Clean up test collection
87+
begin
88+
integration_client.collections['test_products'].delete
89+
rescue StandardError
90+
# Ignore cleanup errors
91+
end
92+
93+
WebMock.enable!
2594
end
2695

2796
describe '#retrieve' do
2897
it 'returns the specified analytics rule' do
29-
stub_request(:get, Typesense::ApiCall.new(typesense.configuration).send(:uri_for, '/analytics/rules/search_suggestions', typesense.configuration.nodes[0]))
30-
.with(headers: {
31-
'X-Typesense-Api-Key' => typesense.configuration.api_key,
32-
'Content-Type': 'application/json'
33-
})
34-
.to_return(status: 200, body: JSON.dump(analytics_rule_data), headers: { 'Content-Type': 'application/json' })
35-
36-
result = analytics_rule.retrieve
98+
result = integration_client.analytics.rules[rule_name].retrieve
3799

38-
expect(result).to eq(analytics_rule_data)
100+
expect(result['name']).to eq(rule_name)
101+
expect(result['type']).to eq('counter')
102+
expect(result['collection']).to eq('test_products')
39103
end
40104
end
41105

42106
describe '#delete' do
43107
it 'deletes the specified analytics rule' do
44-
stub_request(:delete, Typesense::ApiCall.new(typesense.configuration).send(:uri_for, '/analytics/rules/search_suggestions', typesense.configuration.nodes[0]))
45-
.with(headers: {
46-
'X-Typesense-Api-Key' => typesense.configuration.api_key
47-
})
48-
.to_return(status: 200, body: JSON.dump('name' => 'search_suggestions'), headers: { 'Content-Type': 'application/json' })
108+
result = integration_client.analytics.rules[rule_name].delete
109+
expect(result['name']).to eq(rule_name)
110+
111+
# Verify the rule is deleted
112+
expect do
113+
integration_client.analytics.rules[rule_name].retrieve
114+
end.to raise_error(Typesense::Error::RequestMalformed)
115+
end
116+
end
117+
118+
describe '#update' do
119+
it 'updates the specified analytics rule' do
120+
update_params = {
121+
'type' => 'counter',
122+
'collection' => 'test_products',
123+
'event_type' => 'click',
124+
'rule_tag' => 'updated_tag',
125+
'params' => {
126+
'counter_field' => 'popularity',
127+
'weight' => 5
128+
}
129+
}
49130

50-
result = analytics_rule.delete
131+
result = integration_client.analytics.rules[rule_name].update(update_params)
51132

52-
expect(result).to eq('name' => 'search_suggestions')
133+
expect(result['name']).to eq(rule_name)
134+
expect(result['rule_tag']).to eq('updated_tag')
135+
expect(result['params']['weight']).to eq(5)
53136
end
54137
end
55138
end

0 commit comments

Comments
 (0)