mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-08-01 16:04:19 +00:00
245 lines
8.8 KiB
Ruby
245 lines
8.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
require_relative '../../../scripts/internal_events/server'
|
|
|
|
RSpec.describe Server, feature_category: :service_ping do
|
|
include WaitHelpers
|
|
|
|
let(:server) { described_class.new }
|
|
let(:port) { Gitlab::Tracking::Destinations::SnowplowMicro.new.uri.port }
|
|
let(:events) { server.events }
|
|
|
|
let!(:thread) { Thread.new { server.start } }
|
|
|
|
# rubocop:disable RSpec/ExpectOutput -- silencing output, not asserting on it
|
|
before do
|
|
$stderr = StringIO.new
|
|
stub_env('VERIFY_TRACKING', true)
|
|
allow(Addrinfo).to receive(:getaddrinfo).and_call_original
|
|
end
|
|
|
|
after do
|
|
thread.exit
|
|
$stderr = STDERR
|
|
end
|
|
# rubocop:enable RSpec/ExpectOutput
|
|
|
|
describe 'GET /i -> trigger a single event provided through query params (backend)' do
|
|
subject(:response) { await { Net::HTTP.get_response url_for("/i?#{query_params}") } }
|
|
|
|
context 'with an internal event' do
|
|
let(:query_params) { internal_event_fixture('snowplow_events/internal_event_query_params') }
|
|
let(:context) { internal_event_fixture('snowplow_events/internal_event_query_params_decoded.json') }
|
|
let(:expected_event) do
|
|
{
|
|
event: {
|
|
se_category: 'InternalEventTracking',
|
|
se_action: 'g_project_management_issue_created',
|
|
collector_tstamp: '1727475117074',
|
|
se_label: nil,
|
|
se_property: nil,
|
|
se_value: nil,
|
|
contexts: Gitlab::Json.parse(context)
|
|
},
|
|
rawEvent: { parameters: Rack::Utils.parse_query(query_params) }
|
|
}
|
|
end
|
|
|
|
it 'successfully parses event', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/498775' do
|
|
expect(response.code).to eq('200')
|
|
expect(events).to contain_exactly(expected_event)
|
|
end
|
|
end
|
|
|
|
# This case is unexpected in practice, but can be helpful to handle during dev on the server
|
|
# Triggerable in console with `Gitlab::Tracking::Destinations::SnowplowMicro.new.event('category', 'action')`
|
|
context 'with a non-internal event without context key' do
|
|
let(:query_params) { internal_event_fixture('snowplow_events/non_internal_event_without_context') }
|
|
let(:expected_event) do
|
|
{
|
|
event: {
|
|
se_category: 'category',
|
|
se_action: 'super_action_thing',
|
|
collector_tstamp: '1727476712646',
|
|
se_label: nil,
|
|
se_property: nil,
|
|
se_value: nil,
|
|
contexts: nil
|
|
},
|
|
rawEvent: { parameters: Rack::Utils.parse_query(query_params) }
|
|
}
|
|
end
|
|
|
|
it 'successfully parses event', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/498776' do
|
|
expect(response.code).to eq('200')
|
|
expect(events).to contain_exactly(expected_event)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'POST /com.snowplowanalytics.snowplow/tp2 -> trigger events provided through request body (frontend)' do
|
|
subject(:response) { await { Net::HTTP.post url_for('/com.snowplowanalytics.snowplow/tp2'), body } }
|
|
|
|
context 'when triggered on-click' do
|
|
let(:body) { internal_event_fixture('snowplow_events/internal_event_on_click.json') }
|
|
let(:context) { internal_event_fixture('snowplow_events/internal_event_on_click_decoded.json') }
|
|
let(:expected_event) do
|
|
{
|
|
event: {
|
|
se_category: 'projects:blob:show',
|
|
se_action: 'click_blame_control_on_blob_page',
|
|
collector_tstamp: '1727474524024',
|
|
se_label: nil,
|
|
se_property: nil,
|
|
se_value: nil,
|
|
contexts: Gitlab::Json.parse(context)
|
|
},
|
|
rawEvent: { parameters: Gitlab::Json.parse(body)['data'].first }
|
|
}
|
|
end
|
|
|
|
it 'successfully parses event', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/499957' do
|
|
expect(response.code).to eq('200')
|
|
expect(events).to contain_exactly(expected_event)
|
|
end
|
|
end
|
|
|
|
context 'when triggered on-load in a batch' do
|
|
let(:body) { internal_event_fixture('snowplow_events/internal_event_on_load_batched.json') }
|
|
let(:context_1) { internal_event_fixture('snowplow_events/internal_event_on_load_batched_decoded_1.json') }
|
|
let(:context_2) { internal_event_fixture('snowplow_events/internal_event_on_load_batched_decoded_2.json') }
|
|
let(:expected_events) do
|
|
[
|
|
{
|
|
event: {
|
|
se_category: 'admin:dashboard:index',
|
|
se_action: 'view_admin_dashboard_pageload',
|
|
collector_tstamp: '1727473513835',
|
|
se_label: nil,
|
|
se_property: nil,
|
|
se_value: nil,
|
|
contexts: Gitlab::Json.parse(context_1)
|
|
},
|
|
rawEvent: { parameters: Gitlab::Json.parse(body)['data'].first }
|
|
},
|
|
{
|
|
event: {
|
|
se_category: 'admin:dashboard:index',
|
|
se_action: 'render',
|
|
collector_tstamp: '1727473513837',
|
|
se_label: 'version_badge',
|
|
se_property: 'Up to date',
|
|
se_value: nil,
|
|
contexts: Gitlab::Json.parse(context_2)
|
|
},
|
|
rawEvent: { parameters: Gitlab::Json.parse(body)['data'].last }
|
|
}
|
|
]
|
|
end
|
|
|
|
it 'successfully parses event', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/498772' do
|
|
expect(response.code).to eq('200')
|
|
expect(events).to match_array(expected_events)
|
|
end
|
|
end
|
|
|
|
context 'with a structured event but not an internal event' do
|
|
let(:body) { internal_event_fixture('snowplow_events/non_internal_event.json') }
|
|
let(:context) { internal_event_fixture('snowplow_events/non_internal_event_decoded.json') }
|
|
let(:expected_event) do
|
|
{
|
|
event: {
|
|
se_category: 'admin:dashboard:index',
|
|
se_action: 'render',
|
|
collector_tstamp: '1727473512782',
|
|
se_label: 'version_badge',
|
|
se_property: 'Up to date',
|
|
se_value: nil,
|
|
contexts: Gitlab::Json.parse(context)
|
|
},
|
|
rawEvent: { parameters: Gitlab::Json.parse(body)['data'].first }
|
|
}
|
|
end
|
|
|
|
it 'successfully parses event', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/498773' do
|
|
expect(response.code).to eq('200')
|
|
expect(events).to contain_exactly(expected_event)
|
|
end
|
|
end
|
|
|
|
context 'with a non-structured event or an internal event' do
|
|
let(:body) { internal_event_fixture('snowplow_events/non_internal_event_structured.json') }
|
|
|
|
it 'ignores the event', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/498774' do
|
|
expect(response.code).to eq('200')
|
|
expect(events).to be_empty
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'OPTIONS /com.snowplowanalytics.snowplow/tp2' do
|
|
subject(:response) do
|
|
await { Net::HTTP.new('localhost', port).options('/com.snowplowanalytics.snowplow/tp2') }
|
|
end
|
|
|
|
it 'applies the correct headers', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/498779' do
|
|
expect(response.code).to eq('200')
|
|
expect(response.header['Access-Control-Allow-Credentials']).to eq('true')
|
|
expect(response.header['Access-Control-Allow-Headers']).to eq('Content-Type')
|
|
expect(response.header['Access-Control-Allow-Origin']).to eq(Gitlab.config.gitlab.url)
|
|
end
|
|
end
|
|
|
|
describe 'GET /micro/good -> list tracked structured events' do
|
|
subject(:response) { await { Net::HTTP.get_response url_for("/micro/good") } }
|
|
|
|
it 'successfully returns tracked events', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/498777' do
|
|
expect(response.code).to eq('200')
|
|
expect(response.body).to eq("[]")
|
|
end
|
|
|
|
context 'with tracked events' do
|
|
let(:query_params) { internal_event_fixture('snowplow_events/non_internal_event_without_context') }
|
|
|
|
before do
|
|
await { Net::HTTP.get url_for("/i?#{query_params}") }
|
|
end
|
|
|
|
it 'successfully returns tracked events', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/498778' do
|
|
expect(response.code).to eq('200')
|
|
expect(response.body).to eq([{
|
|
event: {
|
|
se_category: 'category',
|
|
se_action: 'super_action_thing',
|
|
collector_tstamp: '1727476712646',
|
|
se_label: nil,
|
|
se_property: nil,
|
|
se_value: nil,
|
|
contexts: nil
|
|
},
|
|
rawEvent: { parameters: Rack::Utils.parse_query(query_params) }
|
|
}].to_json)
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def await
|
|
wait_for('server response to be available', max_wait_time: 2.seconds) do
|
|
yield
|
|
rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL
|
|
nil
|
|
end
|
|
end
|
|
|
|
def url_for(path)
|
|
URI.parse("http://localhost:#{port}#{path}")
|
|
end
|
|
|
|
def internal_event_fixture(filepath)
|
|
File.read(Rails.root.join('spec', 'fixtures', 'scripts', 'internal_events', filepath)).chomp
|
|
end
|
|
end
|