Skip to content

Commit 33ea88d

Browse files
committed
tests(rails): add integration tests for Sequel/Rails tracing
1 parent 48a83af commit 33ea88d

4 files changed

Lines changed: 254 additions & 0 deletions

File tree

sentry-rails/Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,5 @@ gem "benchmark-ips"
6262
gem "benchmark_driver"
6363
gem "benchmark-ipsa"
6464
gem "benchmark-memory"
65+
66+
gem "sequel"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# frozen_string_literal: true
2+
3+
require "sequel"
4+
require "sentry/sequel"
5+
6+
# Controller that uses Sequel for database operations
7+
# Used for testing Sequel tracing integration with Rails
8+
#
9+
# The SEQUEL_DB constant is set up in the test's before(:all) block,
10+
# simulating how a Rails initializer would configure the connection.
11+
class SequelUsersController < ActionController::Base
12+
def index
13+
users = SEQUEL_DB[:users].all
14+
render json: users
15+
end
16+
17+
def create
18+
id = SEQUEL_DB[:users].insert(name: params[:name], email: params[:email])
19+
render json: { id: id, name: params[:name], email: params[:email] }, status: :created
20+
end
21+
22+
def show
23+
user = SEQUEL_DB[:users].where(id: params[:id]).first
24+
if user
25+
render json: user
26+
else
27+
render json: { error: "Not found" }, status: :not_found
28+
end
29+
end
30+
31+
def update
32+
SEQUEL_DB[:users].where(id: params[:id]).update(name: params[:name])
33+
user = SEQUEL_DB[:users].where(id: params[:id]).first
34+
render json: user
35+
end
36+
37+
def destroy
38+
SEQUEL_DB[:users].where(id: params[:id]).delete
39+
head :no_content
40+
end
41+
42+
def exception
43+
SEQUEL_DB[:users].all
44+
raise "Something went wrong!"
45+
end
46+
end

sentry-rails/spec/dummy/test_rails_app/config/application.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ def configure
122122
end
123123
end
124124

125+
# Sequel-specific routes for testing Sequel tracing
126+
scope "/sequel" do
127+
get "/users", to: "sequel_users#index"
128+
post "/users", to: "sequel_users#create"
129+
get "/users/:id", to: "sequel_users#show"
130+
put "/users/:id", to: "sequel_users#update"
131+
delete "/users/:id", to: "sequel_users#destroy"
132+
get "/exception", to: "sequel_users#exception"
133+
end
134+
125135
get "500", to: "hello#reporting"
126136

127137
root to: "hello#world"
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# frozen_string_literal: true
2+
3+
begin
4+
require "simplecov"
5+
SimpleCov.command_name "SequelTracing"
6+
rescue LoadError
7+
end
8+
9+
require "sequel"
10+
require "sentry/sequel"
11+
12+
require_relative "../dummy/test_rails_app/app/controllers/sequel_users_controller"
13+
14+
RSpec.describe "Sequel Tracing with Rails", type: :request do
15+
before(:all) do
16+
SEQUEL_DB = Sequel.sqlite
17+
18+
SEQUEL_DB.create_table :users do
19+
primary_key :id
20+
String :name
21+
String :email
22+
end
23+
24+
SEQUEL_DB[:users].count
25+
SEQUEL_DB.extension(:sentry)
26+
end
27+
28+
after(:all) do
29+
SEQUEL_DB.drop_table?(:users)
30+
Object.send(:remove_const, :SEQUEL_DB)
31+
end
32+
33+
before do
34+
make_basic_app do |config, app|
35+
config.traces_sample_rate = 1.0
36+
config.enabled_patches << :sequel
37+
end
38+
end
39+
40+
let(:transport) { Sentry.get_current_client.transport }
41+
42+
describe "SELECT queries" do
43+
it "creates a transaction with Sequel span for index action" do
44+
get "/sequel/users"
45+
46+
expect(response).to have_http_status(:ok)
47+
expect(transport.events.count).to eq(1)
48+
49+
transaction = transport.events.last.to_h
50+
51+
expect(transaction[:type]).to eq("transaction")
52+
expect(transaction.dig(:contexts, :trace, :op)).to eq("http.server")
53+
54+
sequel_span = transaction[:spans].find { |span| span[:op] == "db.sql.sequel" }
55+
56+
expect(sequel_span).not_to be_nil
57+
expect(sequel_span[:description]).to include("SELECT")
58+
expect(sequel_span[:description]).to include("users")
59+
expect(sequel_span[:origin]).to eq("auto.db.sequel")
60+
expect(sequel_span[:data]["db.system"]).to eq("sqlite")
61+
end
62+
end
63+
64+
describe "INSERT queries" do
65+
it "creates a transaction with Sequel span for create action" do
66+
post "/sequel/users", params: { name: "John Doe", email: "john@example.com" }
67+
68+
expect(response).to have_http_status(:created)
69+
70+
transaction = transport.events.last.to_h
71+
expect(transaction[:type]).to eq("transaction")
72+
73+
insert_span = transaction[:spans].find do |span|
74+
span[:op] == "db.sql.sequel" && span[:description]&.include?("INSERT")
75+
end
76+
77+
expect(insert_span).not_to be_nil
78+
expect(insert_span[:description]).to include("INSERT")
79+
expect(insert_span[:description]).to include("users")
80+
expect(insert_span[:origin]).to eq("auto.db.sequel")
81+
end
82+
end
83+
84+
describe "UPDATE queries" do
85+
it "creates a transaction with Sequel span for update action" do
86+
SEQUEL_DB[:users].insert(name: "Jane Doe", email: "jane@example.com")
87+
88+
put "/sequel/users/1", params: { name: "Jane Smith" }
89+
90+
expect(response).to have_http_status(:ok)
91+
92+
transaction = transport.events.last.to_h
93+
94+
update_span = transaction[:spans].find do |span|
95+
span[:op] == "db.sql.sequel" && span[:description]&.include?("UPDATE")
96+
end
97+
98+
expect(update_span).not_to be_nil
99+
expect(update_span[:description]).to include("UPDATE")
100+
expect(update_span[:description]).to include("users")
101+
end
102+
end
103+
104+
describe "DELETE queries" do
105+
it "creates a transaction with Sequel span for delete action" do
106+
SEQUEL_DB[:users].insert(name: "Delete Me", email: "delete@example.com")
107+
108+
delete "/sequel/users/1"
109+
110+
expect(response).to have_http_status(:no_content)
111+
112+
transaction = transport.events.last.to_h
113+
114+
delete_span = transaction[:spans].find do |span|
115+
span[:op] == "db.sql.sequel" && span[:description]&.include?("DELETE")
116+
end
117+
118+
expect(delete_span).not_to be_nil
119+
expect(delete_span[:description]).to include("DELETE")
120+
expect(delete_span[:description]).to include("users")
121+
end
122+
end
123+
124+
describe "exception handling" do
125+
it "creates both error event and transaction with Sequel span" do
126+
get "/sequel/exception"
127+
128+
expect(response).to have_http_status(:internal_server_error)
129+
130+
expect(transport.events.count).to eq(2)
131+
132+
error_event = transport.events.first.to_h
133+
transaction = transport.events.last.to_h
134+
135+
expect(error_event[:exception][:values].first[:type]).to eq("RuntimeError")
136+
expect(error_event[:exception][:values].first[:value]).to include("Something went wrong!")
137+
138+
sequel_span = transaction[:spans].find { |span| span[:op] == "db.sql.sequel" }
139+
expect(sequel_span).not_to be_nil
140+
expect(sequel_span[:description]).to include("SELECT")
141+
142+
expect(error_event.dig(:contexts, :trace, :trace_id)).to eq(
143+
transaction.dig(:contexts, :trace, :trace_id)
144+
)
145+
end
146+
end
147+
148+
describe "span timing" do
149+
it "records proper start and end timestamps" do
150+
get "/sequel/users"
151+
152+
transaction = transport.events.last.to_h
153+
sequel_span = transaction[:spans].find { |span| span[:op] == "db.sql.sequel" }
154+
155+
expect(sequel_span[:start_timestamp]).not_to be_nil
156+
expect(sequel_span[:timestamp]).not_to be_nil
157+
expect(sequel_span[:start_timestamp]).to be < sequel_span[:timestamp]
158+
end
159+
end
160+
161+
describe "Sequel and ActiveRecord coexistence" do
162+
it "records spans for both database systems in the same application" do
163+
Post.create!(title: "Test Post")
164+
165+
SEQUEL_DB[:users].insert(name: "Sequel User", email: "sequel@example.com")
166+
167+
transport.events.clear
168+
169+
get "/sequel/users"
170+
171+
expect(response).to have_http_status(:ok)
172+
173+
transaction = transport.events.last.to_h
174+
sequel_spans = transaction[:spans].select { |span| span[:op] == "db.sql.sequel" }
175+
176+
expect(sequel_spans.length).to be >= 1
177+
expect(sequel_spans.first[:data]["db.system"]).to eq("sqlite")
178+
end
179+
180+
it "records ActiveRecord spans separately from Sequel spans" do
181+
transport.events.clear
182+
183+
get "/posts"
184+
185+
expect(response).to have_http_status(:internal_server_error) # raises "foo" in PostsController#index
186+
187+
transaction = transport.events.last.to_h
188+
189+
ar_spans = transaction[:spans].select { |span| span[:op] == "db.sql.active_record" }
190+
sequel_spans = transaction[:spans].select { |span| span[:op] == "db.sql.sequel" }
191+
192+
expect(ar_spans.length).to be >= 1
193+
expect(sequel_spans.length).to eq(0)
194+
end
195+
end
196+
end

0 commit comments

Comments
 (0)