From 74e067a0f9156ab4a46d80536982cdcc8d1d1763 Mon Sep 17 00:00:00 2001 From: Sam Pohlenz Date: Mon, 30 Sep 2024 10:20:03 +0930 Subject: [PATCH 1/3] Add specs for generators --- .../trestle/auth/install/install_generator.rb | 4 +- .../trestle/auth/model/model_generator.rb | 2 +- spec/generators/account_generator_spec.rb | 47 ++++++++ spec/generators/admin_generator_spec.rb | 45 +++++++ spec/generators/install_generator_spec.rb | 111 ++++++++++++++++++ spec/generators/model_generator_spec.rb | 37 ++++++ spec/rails_helper.rb | 2 + spec/support/generator_helpers.rb | 21 ++++ trestle-auth.gemspec | 1 + 9 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 spec/generators/account_generator_spec.rb create mode 100644 spec/generators/admin_generator_spec.rb create mode 100644 spec/generators/install_generator_spec.rb create mode 100644 spec/generators/model_generator_spec.rb create mode 100644 spec/support/generator_helpers.rb diff --git a/lib/generators/trestle/auth/install/install_generator.rb b/lib/generators/trestle/auth/install/install_generator.rb index 80d3f44..b4da3c1 100644 --- a/lib/generators/trestle/auth/install/install_generator.rb +++ b/lib/generators/trestle/auth/install/install_generator.rb @@ -28,11 +28,11 @@ def generate_model end def generate_admin - generate "trestle:auth:admin", model, ("--devise" if devise?) + generate "trestle:auth:admin", model, *("--devise" if devise?) end def generate_account - generate "trestle:auth:account", model, ("--devise" if devise?) unless options[:skip_account] + generate "trestle:auth:account", model, *("--devise" if devise?) unless options[:skip_account] end def devise? diff --git a/lib/generators/trestle/auth/model/model_generator.rb b/lib/generators/trestle/auth/model/model_generator.rb index c895d89..5622e96 100644 --- a/lib/generators/trestle/auth/model/model_generator.rb +++ b/lib/generators/trestle/auth/model/model_generator.rb @@ -7,7 +7,7 @@ class ModelGenerator < ::Rails::Generators::Base argument :name, type: :string, default: "Administrator" def create_model - generate "model", "#{name} email:string password_digest:string first_name:string last_name:string remember_token:string remember_token_expires_at:datetime" + generate "model", name, "email:string password_digest:string first_name:string last_name:string remember_token:string remember_token_expires_at:datetime" end def inject_model_methods diff --git a/spec/generators/account_generator_spec.rb b/spec/generators/account_generator_spec.rb new file mode 100644 index 0000000..7fc927b --- /dev/null +++ b/spec/generators/account_generator_spec.rb @@ -0,0 +1,47 @@ +require "spec_helper" + +require "generators/trestle/auth/account/account_generator" + +describe Trestle::Auth::Generators::AccountGenerator, type: :generator do + destination File.expand_path("../../../tmp", __FILE__) + + before do + prepare_destination + end + + context "for a regular user model" do + describe "the generated files" do + before do + run_generator + end + + describe "the admin resource" do + subject { file("app/admin/auth/account_admin.rb") } + + it { is_expected.to exist } + it { is_expected.to have_correct_syntax } + it { is_expected.to contain "Trestle.resource(:account, model: Administrator, scope: Auth, singular: true) do" } + it { is_expected.to contain "params.require(:account).permit(:email, :first_name, :last_name, :password, :password_confirmation)" } + it { is_expected.not_to contain "if Devise.sign_in_after_reset_password" } + end + end + end + + context "for a Devise user model" do + describe "the generated files" do + before do + run_generator %w(User --devise) + end + + describe "the admin resource" do + subject { file("app/admin/auth/account_admin.rb") } + + it { is_expected.to exist } + it { is_expected.to have_correct_syntax } + it { is_expected.to contain "Trestle.resource(:account, model: User, scope: Auth, singular: true) do" } + it { is_expected.to contain "params.require(:account).permit(:email, :password, :password_confirmation)" } + it { is_expected.to contain "if Devise.sign_in_after_reset_password" } + end + end + end +end diff --git a/spec/generators/admin_generator_spec.rb b/spec/generators/admin_generator_spec.rb new file mode 100644 index 0000000..2310bbd --- /dev/null +++ b/spec/generators/admin_generator_spec.rb @@ -0,0 +1,45 @@ +require "spec_helper" + +require "generators/trestle/auth/admin/admin_generator" + +describe Trestle::Auth::Generators::AdminGenerator, type: :generator do + destination File.expand_path("../../../tmp", __FILE__) + + before do + prepare_destination + end + + context "for a regular user model" do + describe "the generated files" do + before do + run_generator + end + + describe "the admin resource" do + subject { file("app/admin/auth/administrators_admin.rb") } + + it { is_expected.to exist } + it { is_expected.to have_correct_syntax } + it { is_expected.to contain "Trestle.resource(:administrators, model: Administrator, scope: Auth) do" } + it { is_expected.not_to contain "if Devise.sign_in_after_reset_password" } + end + end + end + + context "for a Devise user model" do + describe "the generated files" do + before do + run_generator %w(User --devise) + end + + describe "the admin resource" do + subject { file("app/admin/auth/users_admin.rb") } + + it { is_expected.to exist } + it { is_expected.to have_correct_syntax } + it { is_expected.to contain "Trestle.resource(:users, model: User, scope: Auth) do" } + it { is_expected.to contain "if Devise.sign_in_after_reset_password" } + end + end + end +end diff --git a/spec/generators/install_generator_spec.rb b/spec/generators/install_generator_spec.rb new file mode 100644 index 0000000..e970eab --- /dev/null +++ b/spec/generators/install_generator_spec.rb @@ -0,0 +1,111 @@ +require "spec_helper" + +require "generators/trestle/auth/install/install_generator" + +describe Trestle::Auth::Generators::InstallGenerator, type: :generator do + destination File.expand_path("../../../tmp", __FILE__) + + let(:generator_params) { [] } + + before do + prepare_destination + + allow(generator(generator_params)).to receive(:generate) + stub_file "config/initializers/trestle.rb", configuration + end + + let(:configuration) do + <<~EOF + Trestle.configure do |config| + end + EOF + end + + context "regular mode" do + it "generates a model" do + expect(generator(generator_params)).to receive(:generate).with("trestle:auth:model", "Administrator") + run_generator generator_params + end + + it "generates an admin resource" do + expect(generator(generator_params)).to receive(:generate).with("trestle:auth:admin", "Administrator") + run_generator generator_params + end + + it "generates an account resource" do + expect(generator(generator_params)).to receive(:generate).with("trestle:auth:account", "Administrator") + run_generator generator_params + end + + context "when --skip-account is passed" do + let(:generator_params) { %w(--skip-account) } + + it "does not generate an account resource" do + expect(generator(generator_params)).not_to receive(:generate).with("trestle:auth:account", "Administrator") + run_generator generator_params + end + end + + describe "the generated files" do + before do + run_generator generator_params + end + + describe "the Trestle configuration" do + subject { file("config/initializers/trestle.rb") } + + it { is_expected.to exist } + it { is_expected.to have_correct_syntax } + it { is_expected.to contain "config.auth.user_class = -> { Administrator }" } + it { is_expected.to contain "config.auth.user_admin = -> { :\"auth/account\" }" } + it { is_expected.not_to contain "config.auth.backend = :devise" } + end + end + end + + context "Devise mode (--devise)" do + let(:generator_params) { %w(User --devise) } + + it "does not generate a model" do + expect(generator(generator_params)).not_to receive(:generate).with("trestle:auth:model", "User") + run_generator generator_params + end + + it "generates an admin resource" do + expect(generator(generator_params)).to receive(:generate).with("trestle:auth:admin", "User", "--devise") + run_generator generator_params + end + + it "generates an account resource" do + expect(generator(generator_params)).to receive(:generate).with("trestle:auth:account", "User", "--devise") + run_generator generator_params + end + + context "when --skip-account is passed" do + let(:generator_params) { %w(User --devise --skip-account) } + + it "does not generate an account resource" do + expect(generator(generator_params)).not_to receive(:generate).with("trestle:auth:account", "User", "--devise") + run_generator generator_params + end + end + + describe "the generated files" do + before do + run_generator generator_params + end + + describe "the Trestle configuration" do + subject { file("config/initializers/trestle.rb") } + + it { is_expected.to exist } + it { is_expected.to have_correct_syntax } + it { is_expected.to contain "config.auth.backend = :devise" } + it { is_expected.to contain "config.auth.warden.scope = :user" } + it { is_expected.to contain "config.auth.user_class = -> { User }" } + it { is_expected.to contain "config.auth.user_admin = -> { :\"auth/account\" }" } + it { is_expected.to contain "config.auth.authenticate_with = -> { Devise.authentication_keys.first }" } + end + end + end +end diff --git a/spec/generators/model_generator_spec.rb b/spec/generators/model_generator_spec.rb new file mode 100644 index 0000000..d96e678 --- /dev/null +++ b/spec/generators/model_generator_spec.rb @@ -0,0 +1,37 @@ +require "spec_helper" + +require "generators/trestle/auth/model/model_generator" + +describe Trestle::Auth::Generators::ModelGenerator, type: :generator do + destination File.expand_path("../../../tmp", __FILE__) + + before do + prepare_destination + end + + it "generates an ActiveRecord model with the default name" do + expect(generator).to receive(:generate).with("model", "Administrator", "email:string password_digest:string first_name:string last_name:string remember_token:string remember_token_expires_at:datetime") { stub_model_file } + run_generator + end + + it "generates an ActiveRecord model with the specified name" do + expect(generator(%w(TrestleAdmin))).to receive(:generate).with("model", "TrestleAdmin", "email:string password_digest:string first_name:string last_name:string remember_token:string remember_token_expires_at:datetime") { stub_model_file("TrestleAdmin") } + run_generator %w(TrestleAdmin) + end + + describe "the generated files" do + before do + allow(generator).to receive(:generate) { stub_model_file } + run_generator + end + + describe "the model" do + subject { file("app/models/administrator.rb") } + + it { is_expected.to exist } + it { is_expected.to have_correct_syntax } + it { is_expected.to contain "include Trestle::Auth::ModelMethods" } + it { is_expected.to contain "include Trestle::Auth::ModelMethods::Rememberable" } + end + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index e737514..96d2813 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -7,6 +7,7 @@ require 'rspec/rails' require 'show_me_the_cookies' require 'timecop' +require 'ammeter/init' # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. @@ -46,4 +47,5 @@ config.include ShowMeTheCookies, type: :feature config.include Trestle::Auth::Test::LoginHelpers, type: :feature + config.include Trestle::Auth::Test::GeneratorHelpers, type: :generator end diff --git a/spec/support/generator_helpers.rb b/spec/support/generator_helpers.rb new file mode 100644 index 0000000..31b14b5 --- /dev/null +++ b/spec/support/generator_helpers.rb @@ -0,0 +1,21 @@ +module Trestle + module Auth + module Test + module GeneratorHelpers + def stub_model_file(name="Administrator") + stub_file "app/models/#{name.underscore}.rb", <<~EOF + class #{name} < ApplicationRecord + end + EOF + end + + def stub_file(path, contents) + filepath = file(path) + + FileUtils.mkdir_p(filepath.dirname) + File.open(filepath, "w") { |f| f.write(contents) } + end + end + end + end +end diff --git a/trestle-auth.gemspec b/trestle-auth.gemspec index 83a540c..00d5b0b 100644 --- a/trestle-auth.gemspec +++ b/trestle-auth.gemspec @@ -27,4 +27,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency "rspec-rails" spec.add_development_dependency "show_me_the_cookies", "~> 6.0" spec.add_development_dependency "timecop", "~> 0.9.1" + spec.add_development_dependency "ammeter", "~> 1.1.7" end From 2b2a0787bb5abf54dab7cffe0de13588cce62bd8 Mon Sep 17 00:00:00 2001 From: Sam Pohlenz Date: Mon, 30 Sep 2024 10:20:42 +0930 Subject: [PATCH 2/3] Comment out config.auth.user_admin when --skip-account passed to install generator --- .../trestle/auth/install/install_generator.rb | 6 ++++- .../auth/install/templates/basic.rb.erb | 2 +- .../auth/install/templates/devise.rb.erb | 2 +- spec/generators/install_generator_spec.rb | 24 +++++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/generators/trestle/auth/install/install_generator.rb b/lib/generators/trestle/auth/install/install_generator.rb index b4da3c1..750febd 100644 --- a/lib/generators/trestle/auth/install/install_generator.rb +++ b/lib/generators/trestle/auth/install/install_generator.rb @@ -32,13 +32,17 @@ def generate_admin end def generate_account - generate "trestle:auth:account", model, *("--devise" if devise?) unless options[:skip_account] + generate "trestle:auth:account", model, *("--devise" if devise?) unless skip_account? end def devise? options[:devise] end + def skip_account? + options[:skip_account] + end + def configuration_template devise? ? "devise.rb.erb" : "basic.rb.erb" end diff --git a/lib/generators/trestle/auth/install/templates/basic.rb.erb b/lib/generators/trestle/auth/install/templates/basic.rb.erb index 50a8f62..e46dce9 100644 --- a/lib/generators/trestle/auth/install/templates/basic.rb.erb +++ b/lib/generators/trestle/auth/install/templates/basic.rb.erb @@ -11,7 +11,7 @@ config.auth.user_class = -> { <%= model %> } # Specify the Trestle admin for managing the current user (My Account). # -config.auth.user_admin = -> { :"auth/account" } +<% if skip_account? %># <% end %>config.auth.user_admin = -> { :"auth/account" } # Specify the parameter (along with a password) to be used to # authenticate an administrator. Defaults to :email. diff --git a/lib/generators/trestle/auth/install/templates/devise.rb.erb b/lib/generators/trestle/auth/install/templates/devise.rb.erb index 3b25290..2bcf952 100644 --- a/lib/generators/trestle/auth/install/templates/devise.rb.erb +++ b/lib/generators/trestle/auth/install/templates/devise.rb.erb @@ -14,7 +14,7 @@ config.auth.user_class = -> { <%= model %> } # Specify the Trestle admin for managing the current user (My Account). # -config.auth.user_admin = -> { :"auth/account" } +<% if skip_account? %># <% end %>config.auth.user_admin = -> { :"auth/account" } # Specify the parameter (along with a password) to be used to # authenticate an administrator. Defaults to :email if not specified below. diff --git a/spec/generators/install_generator_spec.rb b/spec/generators/install_generator_spec.rb index e970eab..4bb0511 100644 --- a/spec/generators/install_generator_spec.rb +++ b/spec/generators/install_generator_spec.rb @@ -44,6 +44,18 @@ expect(generator(generator_params)).not_to receive(:generate).with("trestle:auth:account", "Administrator") run_generator generator_params end + + describe "the generated files" do + before do + run_generator generator_params + end + + describe "the Trestle configuration" do + subject { file("config/initializers/trestle.rb") } + + it { is_expected.to contain "# config.auth.user_admin = -> { :\"auth/account\" }" } + end + end end describe "the generated files" do @@ -88,6 +100,18 @@ expect(generator(generator_params)).not_to receive(:generate).with("trestle:auth:account", "User", "--devise") run_generator generator_params end + + describe "the generated files" do + before do + run_generator generator_params + end + + describe "the Trestle configuration" do + subject { file("config/initializers/trestle.rb") } + + it { is_expected.to contain "# config.auth.user_admin = -> { :\"auth/account\" }" } + end + end end describe "the generated files" do From 7f707fbd54622917327aef0f9b306fcb53ad9238 Mon Sep 17 00:00:00 2001 From: Sam Pohlenz Date: Mon, 30 Sep 2024 10:39:54 +0930 Subject: [PATCH 3/3] Ensure auth config is inserted before final end in trestle.rb, even when improperly indented --- .../trestle/auth/install/install_generator.rb | 2 +- spec/generators/install_generator_spec.rb | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/generators/trestle/auth/install/install_generator.rb b/lib/generators/trestle/auth/install/install_generator.rb index 750febd..d87e38a 100644 --- a/lib/generators/trestle/auth/install/install_generator.rb +++ b/lib/generators/trestle/auth/install/install_generator.rb @@ -18,7 +18,7 @@ def check_trestle_installed end def insert_configuration - inject_into_file "config/initializers/trestle.rb", before: /^end/ do + inject_into_file "config/initializers/trestle.rb", before: /end(?!.*end.*)/m do format_configuration(template_content(configuration_template)) end end diff --git a/spec/generators/install_generator_spec.rb b/spec/generators/install_generator_spec.rb index 4bb0511..e4f3066 100644 --- a/spec/generators/install_generator_spec.rb +++ b/spec/generators/install_generator_spec.rb @@ -132,4 +132,29 @@ end end end + + context "when the existing Trestle configuration is improperly indented" do + let(:configuration) do + <<~EOF + Trestle.configure do |config| + config.menu do + end + end + EOF + end + + describe "the generated files" do + before do + run_generator generator_params + end + + describe "the Trestle configuration" do + subject { file("config/initializers/trestle.rb") } + + it { is_expected.to have_correct_syntax } + it { is_expected.to contain "# == Authentication Options" } + it { is_expected.not_to contain /config.menu do\n\s*# == Authentication Options/ } + end + end + end end