From 528bff49b74299a20a259ded6048ce8d6efa9f4f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 25 Sep 2026 10:16:06 +0000 Subject: [PATCH] Add DepartmentApi and SentryMetrics regression specs Cover department lookup success/failure shaping used by BuildingsApi room updates, and ensure Sentry metric failures are swallowed so jobs/rake keep running. Co-authored-by: rsmoke --- spec/lib/department_api_spec.rb | 68 ++++++++++++++++++++++ spec/services/sentry_metrics_spec.rb | 84 ++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 spec/lib/department_api_spec.rb create mode 100644 spec/services/sentry_metrics_spec.rb diff --git a/spec/lib/department_api_spec.rb b/spec/lib/department_api_spec.rb new file mode 100644 index 00000000..8f1e251f --- /dev/null +++ b/spec/lib/department_api_spec.rb @@ -0,0 +1,68 @@ +require "rails_helper" + +RSpec.describe DepartmentApi do + let(:client) { instance_double(UmApi::Connection) } + let(:api) { described_class.new("dept-token") } + + before do + allow(UmApi::Connection).to receive(:new) + .with(access_token: "dept-token", scope: "department") + .and_return(client) + end + + describe "#get_departments_info" do + it "returns the normalized success payload with DepartmentList as data" do + allow(client).to receive(:get_json).and_return( + "success" => true, + "data" => { + "DepartmentList" => { + "DeptData" => [{"DeptDescription" => "Mathematics", "DeptId" => "123"}] + } + } + ) + + result = api.get_departments_info("Mathematics") + + expect(result).to eq( + "success" => true, + "errorcode" => "", + "error" => "", + "data" => { + "DeptData" => [{"DeptDescription" => "Mathematics", "DeptId" => "123"}] + } + ) + expect(client).to have_received(:get_json).with( + "#{DepartmentApi::BASE_URL}/DeptData", + query: {DeptDescription: "Mathematics"} + ) + end + + it "passes through unsuccessful API responses without rewriting them" do + failure = { + "success" => false, + "errorcode" => "HTTP 404", + "error" => "Department not found", + "data" => {} + } + allow(client).to receive(:get_json).and_return(failure) + + expect(api.get_departments_info("Missing Dept")).to eq(failure) + end + end + + describe "#get_all_departments_info" do + it "delegates to paginated_get with the DepartmentList/DeptData collection path" do + pages = { + "success" => true, + "data" => [{"DeptDescription" => "History", "DeptId" => "9"}] + } + allow(client).to receive(:paginated_get).and_return(pages) + + expect(api.get_all_departments_info).to eq(pages) + expect(client).to have_received(:paginated_get).with( + "#{DepartmentApi::BASE_URL}/DeptData", + collection_path: %w[DepartmentList DeptData] + ) + end + end +end diff --git a/spec/services/sentry_metrics_spec.rb b/spec/services/sentry_metrics_spec.rb new file mode 100644 index 00000000..3104e490 --- /dev/null +++ b/spec/services/sentry_metrics_spec.rb @@ -0,0 +1,84 @@ +require "rails_helper" + +RSpec.describe SentryMetrics do + before do + allow(Rails.logger).to receive(:debug) + end + + describe ".count" do + it "forwards the metric with app and environment attributes" do + allow(Sentry::Metrics).to receive(:count) + + described_class.count("jobs.started", value: 2, attributes: {job: "SyncJob"}) + + expect(Sentry::Metrics).to have_received(:count).with( + "jobs.started", + value: 2, + attributes: hash_including(app: "mi_classrooms", environment: Rails.env, job: "SyncJob") + ) + end + + it "swallows Sentry failures so callers are not interrupted" do + allow(Sentry::Metrics).to receive(:count).and_raise(StandardError, "sentry down") + + expect { described_class.count("jobs.started") }.not_to raise_error + expect(Rails.logger).to have_received(:debug).with(/Sentry metric count failed for jobs.started/) + end + end + + describe ".gauge" do + it "forwards an optional unit and merges base attributes" do + allow(Sentry::Metrics).to receive(:gauge) + + described_class.gauge("queue.depth", 4, unit: "none", attributes: {queue: "default"}) + + expect(Sentry::Metrics).to have_received(:gauge).with( + "queue.depth", + 4.0, + unit: "none", + attributes: hash_including(app: "mi_classrooms", environment: Rails.env, queue: "default") + ) + end + + it "swallows Sentry failures so callers are not interrupted" do + allow(Sentry::Metrics).to receive(:gauge).and_raise(StandardError, "sentry down") + + expect { described_class.gauge("queue.depth", 1) }.not_to raise_error + expect(Rails.logger).to have_received(:debug).with(/Sentry metric gauge failed for queue.depth/) + end + end + + describe ".distribution" do + it "forwards an optional unit and merges base attributes" do + allow(Sentry::Metrics).to receive(:distribution) + + described_class.distribution("jobs.duration", 12.5, unit: "millisecond", attributes: {job: "SyncJob"}) + + expect(Sentry::Metrics).to have_received(:distribution).with( + "jobs.duration", + 12.5, + unit: "millisecond", + attributes: hash_including(app: "mi_classrooms", environment: Rails.env, job: "SyncJob") + ) + end + + it "omits the unit keyword when none is provided" do + allow(Sentry::Metrics).to receive(:distribution) + + described_class.distribution("jobs.duration", 3) + + expect(Sentry::Metrics).to have_received(:distribution).with( + "jobs.duration", + 3.0, + attributes: hash_including(app: "mi_classrooms", environment: Rails.env) + ) + end + + it "swallows Sentry failures so callers are not interrupted" do + allow(Sentry::Metrics).to receive(:distribution).and_raise(StandardError, "sentry down") + + expect { described_class.distribution("jobs.duration", 1) }.not_to raise_error + expect(Rails.logger).to have_received(:debug).with(/Sentry metric distribution failed for jobs.duration/) + end + end +end