From 37f449a69fd8b38309ecc794341cdee9a7799a2c Mon Sep 17 00:00:00 2001 From: Sebastian Noe Date: Wed, 13 May 2026 12:06:28 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20add=20team=20factory=20for=20RSpec=20(22?= =?UTF-8?q?1=20failures=20=E2=86=92=200)=20(#12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: add team factory and associate with user factory The Teams feature added a NOT NULL team_id constraint on users. The user factory was missing this association, causing 221/230 spec failures. * fix: make local CI match GitHub Actions 1:1 - Dockerfile.ci: add chromium, chromedriver, pdfium (same as remote CI) - docker-compose.ci.yml: fix PG18 tmpfs mount path (/var/lib/postgresql) - .githooks/pre-push: run FULL CI suite (lint + brakeman + rspec) Local CI results: 230 examples, 2 failures (system spec browser issues, pre-existing — same tests fail on remote CI too) * fix: resolve failing RSpec tests (setup + signing form) - setup_spec.rb:28: move team assignment before @account.valid? check to prevent cascading validation failure from missing team association - signing_form_spec.rb:1151: fix race condition by waiting for page content before asserting job enqueue; correct expected message text - db/schema.rb: update to reflect teams migrations * fix: resolve remaining flaky test failures - dashboard_spec.rb: use deterministic template name to prevent Faker::Book.title collisions between account templates - rails_helper.rb: increase Cuprite timeout from 20s to 30s to prevent PendingConnectionsError on first page load in CI --------- Co-authored-by: Sebastian Noe --- .githooks/pre-push | 23 +++++++++++++++++----- Dockerfile.ci | 11 ++++++++++- app/controllers/setup_controller.rb | 4 ++-- db/schema.rb | 30 ++++++++++++++++++++++++++++- docker-compose.ci.yml | 2 +- spec/factories/teams.rb | 8 ++++++++ spec/factories/users.rb | 1 + spec/rails_helper.rb | 4 ++-- spec/system/dashboard_spec.rb | 2 +- spec/system/signing_form_spec.rb | 7 ++++--- 10 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 spec/factories/teams.rb diff --git a/.githooks/pre-push b/.githooks/pre-push index b7d49c21..d1d2b6fd 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -1,14 +1,27 @@ #!/bin/sh -# Pre-push hook: runs linting via Docker before pushing to GitHub. -# Ensures Rubocop, ERBLint, and ESLint pass locally. +# Pre-push hook: runs the FULL CI suite via Docker before pushing to GitHub. +# Mirrors GitHub Actions exactly: Rubocop, ERBLint, ESLint, Brakeman, RSpec. # Skip with: git push --no-verify # # Enable this hook: git config core.hooksPath .githooks set -e -echo "🔍 Running lint checks before push..." +echo "🔍 Running full CI suite before push..." -docker compose -f docker-compose.ci.yml build lint --quiet 2>/dev/null +docker compose -f docker-compose.ci.yml build --quiet 2>/dev/null + +echo "━━━ Lint (Rubocop + ERBLint + ESLint) ━━━" docker compose -f docker-compose.ci.yml run --rm --no-deps lint -echo "✅ All lint checks passed." +echo "" +echo "━━━ Brakeman (Security) ━━━" +docker compose -f docker-compose.ci.yml run --rm --no-deps brakeman + +echo "" +echo "━━━ RSpec ━━━" +docker compose -f docker-compose.ci.yml run --rm rspec + +docker compose -f docker-compose.ci.yml down --volumes 2>/dev/null + +echo "" +echo "✅ Full CI suite passed. Push allowed." diff --git a/Dockerfile.ci b/Dockerfile.ci index 7691ab1d..6c8e869f 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -12,7 +12,16 @@ RUN apk add --no-cache \ yaml-dev \ nodejs \ yarn \ - vips-dev + vips-dev \ + chromium \ + chromium-chromedriver \ + && wget -O pdfium-linux.tgz "https://github.com/bblanchon/pdfium-binaries/releases/latest/download/pdfium-linux-musl-x64.tgz" \ + && mkdir -p /pdfium && tar -xzf pdfium-linux.tgz -C /pdfium \ + && cp /pdfium/lib/libpdfium.so /usr/lib/ \ + && rm -rf pdfium-linux.tgz /pdfium + +ENV CHROME_BIN=/usr/bin/chromium-browser +ENV CHROMIUM_FLAGS="--no-sandbox --headless --disable-gpu" COPY Gemfile Gemfile.lock ./ RUN bundle install --jobs 4 --retry 3 diff --git a/app/controllers/setup_controller.rb b/app/controllers/setup_controller.rb index f1147e37..69586dab 100644 --- a/app/controllers/setup_controller.rb +++ b/app/controllers/setup_controller.rb @@ -20,6 +20,8 @@ class SetupController < ApplicationController @user = @account.users.new(user_params) @encrypted_config = EncryptedConfig.new(encrypted_config_params) + @user.team = @account.teams.new(name: 'Default') + unless URI.parse(encrypted_config_params[:value].to_s).class.in?([URI::HTTP, URI::HTTPS]) @encrypted_config.errors.add(:value, I18n.t('should_be_a_valid_url')) @@ -28,8 +30,6 @@ class SetupController < ApplicationController return render :index, status: :unprocessable_content unless @account.valid? - @user.team = @account.teams.new(name: 'Default') - if @user.save encrypted_configs = [ { key: EncryptedConfig::APP_URL_KEY, value: encrypted_config_params[:value] }, diff --git a/db/schema.rb b/db/schema.rb index d8d7d1cb..8adda57c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do +ActiveRecord::Schema[8.1].define(version: 2026_05_08_100002) do # These are extensions that must be enabled in order to support this database enable_extension "btree_gin" enable_extension "pg_catalog.plpgsql" @@ -357,6 +357,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do t.string "slug", null: false t.string "source", null: false t.string "submitters_order", null: false + t.bigint "team_id" t.text "template_fields" t.bigint "template_id" t.text "template_schema" @@ -369,6 +370,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do t.index ["account_id", "template_id", "id"], name: "index_submissions_on_account_id_and_template_id_and_id_archived", where: "(archived_at IS NOT NULL)" t.index ["created_by_user_id"], name: "index_submissions_on_created_by_user_id" t.index ["slug"], name: "index_submissions_on_slug", unique: true + t.index ["team_id"], name: "index_submissions_on_team_id" t.index ["template_id"], name: "index_submissions_on_template_id" end @@ -400,6 +402,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do t.datetime "sent_at" t.string "slug", null: false t.bigint "submission_id", null: false + t.bigint "team_id" t.string "timezone" t.string "ua" t.datetime "updated_at", null: false @@ -411,6 +414,19 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do t.index ["external_id"], name: "index_submitters_on_external_id" t.index ["slug"], name: "index_submitters_on_slug", unique: true t.index ["submission_id"], name: "index_submitters_on_submission_id" + t.index ["team_id"], name: "index_submitters_on_team_id" + end + + create_table "teams", force: :cascade do |t| + t.bigint "account_id", null: false + t.datetime "archived_at" + t.datetime "created_at", null: false + t.string "name", null: false + t.datetime "updated_at", null: false + t.string "uuid", null: false + t.index ["account_id", "name"], name: "index_teams_on_account_id_and_name", unique: true, where: "(archived_at IS NULL)" + t.index ["account_id"], name: "index_teams_on_account_id" + t.index ["uuid"], name: "index_teams_on_uuid", unique: true end create_table "template_accesses", force: :cascade do |t| @@ -428,10 +444,12 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do t.datetime "created_at", null: false t.string "name", null: false t.bigint "parent_folder_id" + t.bigint "team_id" t.datetime "updated_at", null: false t.index ["account_id"], name: "index_template_folders_on_account_id" t.index ["author_id"], name: "index_template_folders_on_author_id" t.index ["parent_folder_id"], name: "index_template_folders_on_parent_folder_id" + t.index ["team_id"], name: "index_template_folders_on_team_id" end create_table "template_sharings", force: :cascade do |t| @@ -472,6 +490,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do t.string "slug", null: false t.text "source", null: false t.text "submitters", null: false + t.bigint "team_id" t.datetime "updated_at", null: false t.text "variables_schema" t.index ["account_id", "folder_id", "id"], name: "index_templates_on_account_id_and_folder_id_and_id", where: "(archived_at IS NULL)" @@ -481,6 +500,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do t.index ["external_id"], name: "index_templates_on_external_id" t.index ["folder_id"], name: "index_templates_on_folder_id" t.index ["slug"], name: "index_templates_on_slug", unique: true + t.index ["team_id"], name: "index_templates_on_team_id" end create_table "user_configs", force: :cascade do |t| @@ -518,6 +538,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do t.string "reset_password_token" t.string "role", null: false t.integer "sign_in_count", default: 0, null: false + t.bigint "team_id", null: false t.string "unconfirmed_email" t.string "unlock_token" t.datetime "updated_at", null: false @@ -525,6 +546,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do t.index ["account_id"], name: "index_users_on_account_id" t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true + t.index ["team_id"], name: "index_users_on_team_id" t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true t.index ["uuid"], name: "index_users_on_uuid", unique: true end @@ -591,12 +613,16 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do add_foreign_key "submission_events", "accounts" add_foreign_key "submission_events", "submissions" add_foreign_key "submission_events", "submitters" + add_foreign_key "submissions", "teams" add_foreign_key "submissions", "templates" add_foreign_key "submissions", "users", column: "created_by_user_id" add_foreign_key "submitter_versions", "submitters" add_foreign_key "submitters", "submissions" + add_foreign_key "submitters", "teams" + add_foreign_key "teams", "accounts" add_foreign_key "template_accesses", "templates" add_foreign_key "template_folders", "accounts" + add_foreign_key "template_folders", "teams" add_foreign_key "template_folders", "template_folders", column: "parent_folder_id" add_foreign_key "template_folders", "users", column: "author_id" add_foreign_key "template_sharings", "templates" @@ -604,9 +630,11 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do add_foreign_key "template_versions", "templates" add_foreign_key "template_versions", "users", column: "author_id" add_foreign_key "templates", "accounts" + add_foreign_key "templates", "teams" add_foreign_key "templates", "template_folders", column: "folder_id" add_foreign_key "templates", "users", column: "author_id" add_foreign_key "user_configs", "users" add_foreign_key "users", "accounts" + add_foreign_key "users", "teams" add_foreign_key "webhook_urls", "accounts" end diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index d4cc833e..3353e73f 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -36,5 +36,5 @@ services: timeout: 5s retries: 5 tmpfs: - - /var/lib/postgresql/data + - /var/lib/postgresql diff --git a/spec/factories/teams.rb b/spec/factories/teams.rb new file mode 100644 index 00000000..0cf7eaf4 --- /dev/null +++ b/spec/factories/teams.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :team do + account + name { Faker::Team.name } + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 007f8a50..9f6fdc1e 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -3,6 +3,7 @@ FactoryBot.define do factory :user do account + team { association :team, account: account } first_name { Faker::Name.first_name } last_name { Faker::Name.last_name } password { 'password' } diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 74284e07..55f971d9 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -22,8 +22,8 @@ Capybara.disable_animation = true Capybara.register_driver(:headless_cuprite) do |app| Capybara::Cuprite::Driver.new(app, window_size: [1200, 800], - process_timeout: 20, - timeout: 20, + process_timeout: 30, + timeout: 30, js_errors: true, browser_options: { 'no-sandbox' => nil }) end diff --git a/spec/system/dashboard_spec.rb b/spec/system/dashboard_spec.rb index beca9606..d9f3f459 100644 --- a/spec/system/dashboard_spec.rb +++ b/spec/system/dashboard_spec.rb @@ -19,7 +19,7 @@ RSpec.describe 'Dashboard Page' do context 'when there are templates' do let!(:authors) { create_list(:user, 5, account:) } let!(:templates) { authors.map { |author| create(:template, account:, author:) } } - let!(:other_template) { create(:template, account: create(:user).account) } + let!(:other_template) { create(:template, name: 'Other Account Template XYZ', account: create(:user).account) } before do visit root_path diff --git a/spec/system/signing_form_spec.rb b/spec/system/signing_form_spec.rb index 88884d6d..b053369b 100644 --- a/spec/system/signing_form_spec.rb +++ b/spec/system/signing_form_spec.rb @@ -1153,9 +1153,10 @@ RSpec.describe 'Signing Form' do click_on 'next' draw_canvas - expect do - click_on 'Sign and Complete' - end.to change(ProcessSubmitterCompletionJob.jobs, :size).by(1) + click_on 'Sign and Complete' + + expect(page).to have_content('Document has been signed!') + expect(ProcessSubmitterCompletionJob.jobs.size).to eq(1) end end