From dc0d700f16b967f98369d481cc66f9b288032903 Mon Sep 17 00:00:00 2001 From: hhff Date: Fri, 31 Jul 2026 13:55:02 -0700 Subject: [PATCH] fix(forecast,twist): https base_uri so writes don't die on the http->https redirect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stacks::Forecast declared base_uri 'api.forecastapp.com' (no scheme), so HTTParty used http://. Forecast 301-redirects to https; GETs survive that but a redirected POST arrives malformed and Forecast returns 400. Reads worked, writes silently failed — create_assignment raised the 400 and RecurringAssignment#materialize! swallowed it (Sentry), so nothing reached Forecast and nothing surfaced in the UI. Verified live: via the class (http->301->https) POST /assignments => 400; direct https => 201. Stacks::Twist had the same scheme-less base_uri and also POSTs — fixed for the same reason. All other API clients already used https. Added a guard test pinning every HTTParty client to an https base_uri. Co-Authored-By: Claude Opus 4.8 --- lib/stacks/forecast.rb | 2 +- lib/stacks/twist.rb | 2 +- test/lib/stacks/https_base_uri_test.rb | 30 ++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 test/lib/stacks/https_base_uri_test.rb diff --git a/lib/stacks/forecast.rb b/lib/stacks/forecast.rb index 3bb2b0a8..54c82c1e 100644 --- a/lib/stacks/forecast.rb +++ b/lib/stacks/forecast.rb @@ -1,6 +1,6 @@ class Stacks::Forecast include HTTParty - base_uri 'api.forecastapp.com' + base_uri 'https://api.forecastapp.com' def initialize() @headers = { diff --git a/lib/stacks/twist.rb b/lib/stacks/twist.rb index 2a961129..4abe1598 100644 --- a/lib/stacks/twist.rb +++ b/lib/stacks/twist.rb @@ -1,6 +1,6 @@ class Stacks::Twist include HTTParty - base_uri 'api.twist.com/api/v3' + base_uri 'https://api.twist.com/api/v3' def initialize() @headers = { diff --git a/test/lib/stacks/https_base_uri_test.rb b/test/lib/stacks/https_base_uri_test.rb new file mode 100644 index 00000000..5898c03f --- /dev/null +++ b/test/lib/stacks/https_base_uri_test.rb @@ -0,0 +1,30 @@ +require "test_helper" + +# Regression guard for a silent, production-only class of bug: +# +# A scheme-less HTTParty `base_uri` (e.g. "api.forecastapp.com") defaults to +# http://. Forecast (and Twist) 301-redirect http -> https. GETs survive the +# redirect, but a redirected POST/PUT/DELETE arrives malformed and the API +# returns 400 — so READS work while WRITES silently fail. Stubbed unit tests +# never catch it because they don't hit the network; it only surfaces against +# the live API. `Stacks::Forecast#create_assignment` hitting this is exactly +# what stopped recurring-assignment materialization from reaching Forecast. +# +# Pin every HTTParty API client to an explicit https base_uri. +class Stacks::HttpsBaseUriTest < ActiveSupport::TestCase + CLIENTS = [ + Stacks::Forecast, + Stacks::Twist, + Stacks::Runn, + Stacks::Deel, + Stacks::Notion, + Stacks::Apollo, + ].freeze + + test "every HTTParty API client uses an https base_uri (writes must not ride an http->https redirect)" do + CLIENTS.each do |klass| + assert klass.base_uri.to_s.start_with?("https://"), + "#{klass}.base_uri must be https to avoid write-mangling redirects; got #{klass.base_uri.inspect}" + end + end +end