forked from fixin.me/fixin.me
Compare commits
3 Commits
fix-sqlite
...
pr69-multi
| Author | SHA1 | Date | |
|---|---|---|---|
| 39900958ad | |||
| 80e0170876 | |||
| 0085377fad |
74
.gitea/workflows/test.yml
Normal file
74
.gitea/workflows/test.yml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-sqlite:
|
||||||
|
name: Tests (SQLite)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: '3.3'
|
||||||
|
bundler-cache: true
|
||||||
|
env:
|
||||||
|
BUNDLE_WITH: "sqlite:development:test"
|
||||||
|
|
||||||
|
- name: Set up test database
|
||||||
|
run: bin/rails db:create db:schema:load
|
||||||
|
env:
|
||||||
|
RAILS_ENV: test
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: bin/rails test
|
||||||
|
env:
|
||||||
|
RAILS_ENV: test
|
||||||
|
CI: "true"
|
||||||
|
|
||||||
|
test-mysql:
|
||||||
|
name: Tests (MySQL)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
env:
|
||||||
|
MYSQL_ROOT_PASSWORD: ""
|
||||||
|
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||||
|
MYSQL_DATABASE: fixin_test
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
options: >-
|
||||||
|
--health-cmd="mysqladmin ping"
|
||||||
|
--health-interval=10s
|
||||||
|
--health-timeout=5s
|
||||||
|
--health-retries=3
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: '3.3'
|
||||||
|
bundler-cache: true
|
||||||
|
env:
|
||||||
|
BUNDLE_WITH: "mysql:development:test"
|
||||||
|
|
||||||
|
- name: Set up test database
|
||||||
|
run: bin/rails db:schema:load
|
||||||
|
env:
|
||||||
|
RAILS_ENV: test
|
||||||
|
DB_ADAPTER: mysql
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: bin/rails test
|
||||||
|
env:
|
||||||
|
RAILS_ENV: test
|
||||||
|
CI: "true"
|
||||||
|
DB_ADAPTER: mysql
|
||||||
@@ -16,7 +16,7 @@ class Quantity < ApplicationRecord
|
|||||||
end
|
end
|
||||||
validates :name, presence: true, uniqueness: {scope: [:user_id, :parent_id]},
|
validates :name, presence: true, uniqueness: {scope: [:user_id, :parent_id]},
|
||||||
length: {maximum: type_for_attribute(:name).limit}
|
length: {maximum: type_for_attribute(:name).limit}
|
||||||
validates :description, length: {maximum: type_for_attribute(:description).limit} if type_for_attribute(:description).limit
|
validates :description, length: {maximum: type_for_attribute(:description).limit}
|
||||||
|
|
||||||
# Update :depths of progenies after parent change
|
# Update :depths of progenies after parent change
|
||||||
before_save if: :parent_changed? do
|
before_save if: :parent_changed? do
|
||||||
@@ -61,22 +61,18 @@ class Quantity < ApplicationRecord
|
|||||||
|
|
||||||
# Return: ordered [sub]hierarchy
|
# Return: ordered [sub]hierarchy
|
||||||
scope :ordered, ->(root: nil, include_root: true) {
|
scope :ordered, ->(root: nil, include_root: true) {
|
||||||
q_ordered = Arel::Table.new('q_ordered')
|
numbered = Arel::Table.new('numbered')
|
||||||
cast_type = connection.adapter_name == 'Mysql2' ? 'BINARY' : 'BLOB'
|
|
||||||
|
|
||||||
self.model.with(numbered: numbered(:parent_id, :name)).with_recursive(q_ordered: [
|
self.model.with(numbered: numbered(:parent_id, :name)).with_recursive(arel_table.name => [
|
||||||
Quantity.from(Arel::Table.new('numbered').as(arel_table.name))
|
numbered.project(
|
||||||
.select(
|
numbered[Arel.star],
|
||||||
arel_table[Arel.star],
|
numbered.cast(numbered[:child_number], 'BINARY').as('path')
|
||||||
arel_table.cast(arel_table[:child_number], cast_type).as('path')
|
).where(numbered[root && include_root ? :id : :parent_id].eq(root)),
|
||||||
).where(arel_table[root && include_root ? :id : :parent_id].eq(root)),
|
numbered.project(
|
||||||
Quantity.from(Arel::Table.new('numbered').as(arel_table.name))
|
numbered[Arel.star],
|
||||||
.joins("INNER JOIN q_ordered ON quantities.parent_id = q_ordered.id")
|
arel_table[:path].concat(numbered[:child_number])
|
||||||
.select(
|
).join(arel_table).on(numbered[:parent_id].eq(arel_table[:id]))
|
||||||
arel_table[Arel.star],
|
]).order(arel_table[:path])
|
||||||
q_ordered[:path].concat(arel_table[:child_number])
|
|
||||||
)
|
|
||||||
]).from(q_ordered.as(arel_table.name)).order(arel_table[:path])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: extract named functions to custom Arel extension
|
# TODO: extract named functions to custom Arel extension
|
||||||
@@ -84,24 +80,20 @@ class Quantity < ApplicationRecord
|
|||||||
# be merged with :ordered
|
# be merged with :ordered
|
||||||
# https://gist.github.com/ProGM/c6df08da14708dcc28b5ca325df37ceb#extending-arel
|
# https://gist.github.com/ProGM/c6df08da14708dcc28b5ca325df37ceb#extending-arel
|
||||||
scope :numbered, ->(parent_column, order_column) {
|
scope :numbered, ->(parent_column, order_column) {
|
||||||
row_number = Arel::Nodes::NamedFunction.new('ROW_NUMBER', [])
|
select(
|
||||||
.over(Arel::Nodes::Window.new.partition(parent_column).order(order_column))
|
arel_table[Arel.star],
|
||||||
width = Arel::SelectManager.new.project(
|
Arel::Nodes::NamedFunction.new(
|
||||||
|
'LPAD',
|
||||||
|
[
|
||||||
|
Arel::Nodes::NamedFunction.new('ROW_NUMBER', [])
|
||||||
|
.over(Arel::Nodes::Window.new.partition(parent_column).order(order_column)),
|
||||||
|
Arel::SelectManager.new.project(
|
||||||
Arel::Nodes::NamedFunction.new('LENGTH', [Arel.star.count])
|
Arel::Nodes::NamedFunction.new('LENGTH', [Arel.star.count])
|
||||||
).from(arel_table)
|
).from(arel_table),
|
||||||
|
Arel::Nodes.build_quoted('0')
|
||||||
pad_expr = if connection.adapter_name == 'Mysql2'
|
],
|
||||||
Arel::Nodes::NamedFunction.new('LPAD', [row_number, width, Arel::Nodes.build_quoted('0')])
|
).as('child_number')
|
||||||
else
|
|
||||||
# SQLite: printf('%0' || width || 'd', row_number)
|
|
||||||
fmt = Arel::Nodes::InfixOperation.new('||',
|
|
||||||
Arel::Nodes::InfixOperation.new('||', Arel::Nodes.build_quoted('%0'), width),
|
|
||||||
Arel::Nodes.build_quoted('d')
|
|
||||||
)
|
)
|
||||||
Arel::Nodes::NamedFunction.new('printf', [fmt, row_number])
|
|
||||||
end
|
|
||||||
|
|
||||||
select(arel_table[Arel.star], pad_expr.as('child_number'))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
@@ -110,7 +102,7 @@ class Quantity < ApplicationRecord
|
|||||||
|
|
||||||
def to_s_with_depth
|
def to_s_with_depth
|
||||||
# em space, U+2003
|
# em space, U+2003
|
||||||
' ' * depth + name
|
' ' * depth + name
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroyable?
|
def destroyable?
|
||||||
@@ -123,18 +115,16 @@ class Quantity < ApplicationRecord
|
|||||||
|
|
||||||
# Common ancestors, assuming node is a descendant of itself
|
# Common ancestors, assuming node is a descendant of itself
|
||||||
scope :common_ancestors, ->(of) {
|
scope :common_ancestors, ->(of) {
|
||||||
q_common = Arel::Table.new('q_common')
|
selected = Arel::Table.new('selected')
|
||||||
|
|
||||||
# Take unique IDs, so self can be called with parent nodes of collection to
|
# Take unique IDs, so self can be called with parent nodes of collection to
|
||||||
# get common ancestors of collection _excluding_ nodes in collection.
|
# get common ancestors of collection _excluding_ nodes in collection.
|
||||||
uniq_of = of.uniq
|
uniq_of = of.uniq
|
||||||
model.with(selected: self).with_recursive(q_common: [
|
model.with(selected: self).with_recursive(arel_table.name => [
|
||||||
Quantity.from(Arel::Table.new('selected').as(arel_table.name))
|
selected.project(selected[Arel.star]).where(selected[:id].in(uniq_of)),
|
||||||
.where(arel_table[:id].in(uniq_of)),
|
selected.project(selected[Arel.star])
|
||||||
Quantity.from(Arel::Table.new('selected').as(arel_table.name))
|
.join(arel_table).on(selected[:id].eq(arel_table[:parent_id]))
|
||||||
.joins("INNER JOIN q_common ON selected.id = q_common.parent_id")
|
|
||||||
]).select(arel_table[Arel.star])
|
]).select(arel_table[Arel.star])
|
||||||
.from(q_common.as(arel_table.name))
|
|
||||||
.group(column_names)
|
.group(column_names)
|
||||||
.having(arel_table[:id].count.eq(uniq_of.size))
|
.having(arel_table[:id].count.eq(uniq_of.size))
|
||||||
.order(arel_table[:depth].desc)
|
.order(arel_table[:depth].desc)
|
||||||
@@ -142,9 +132,13 @@ class Quantity < ApplicationRecord
|
|||||||
|
|
||||||
# Return: successive record in order of appearance; used for partial view reload
|
# Return: successive record in order of appearance; used for partial view reload
|
||||||
def successive
|
def successive
|
||||||
ordered = user.quantities.ordered.to_a
|
quantities = Quantity.arel_table
|
||||||
idx = ordered.index { |q| q.id == id }
|
Quantity.with(
|
||||||
ordered[idx + 1] if idx
|
quantities: user.quantities.ordered.select(
|
||||||
|
quantities[Arel.star],
|
||||||
|
Arel::Nodes::NamedFunction.new('LAG', [quantities[:id]]).over.as('lag_id')
|
||||||
|
)
|
||||||
|
).where(quantities[:lag_id].eq(id)).first
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_progenies
|
def with_progenies
|
||||||
@@ -157,14 +151,13 @@ class Quantity < ApplicationRecord
|
|||||||
|
|
||||||
# Return: record with ID `of` with its ancestors, sorted by :depth
|
# Return: record with ID `of` with its ancestors, sorted by :depth
|
||||||
scope :with_ancestors, ->(of) {
|
scope :with_ancestors, ->(of) {
|
||||||
q_ancestors = Arel::Table.new('q_ancestors')
|
selected = Arel::Table.new('selected')
|
||||||
|
|
||||||
model.with(selected: self).with_recursive(q_ancestors: [
|
model.with(selected: self).with_recursive(arel_table.name => [
|
||||||
Quantity.from(Arel::Table.new('selected').as(arel_table.name))
|
selected.project(selected[Arel.star]).where(selected[:id].eq(of)),
|
||||||
.where(arel_table[:id].eq(of)),
|
selected.project(selected[Arel.star])
|
||||||
Quantity.from(Arel::Table.new('selected').as(arel_table.name))
|
.join(arel_table).on(selected[:id].eq(arel_table[:parent_id]))
|
||||||
.joins("INNER JOIN q_ancestors ON selected.id = q_ancestors.parent_id")
|
])
|
||||||
]).from(q_ancestors.as(arel_table.name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Return: ancestors of (possibly destroyed) self
|
# Return: ancestors of (possibly destroyed) self
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Unit < ApplicationRecord
|
|||||||
end
|
end
|
||||||
validates :symbol, presence: true, uniqueness: {scope: :user_id},
|
validates :symbol, presence: true, uniqueness: {scope: :user_id},
|
||||||
length: {maximum: type_for_attribute(:symbol).limit}
|
length: {maximum: type_for_attribute(:symbol).limit}
|
||||||
validates :description, length: {maximum: type_for_attribute(:description).limit} if type_for_attribute(:description).limit
|
validates :description, length: {maximum: type_for_attribute(:description).limit}
|
||||||
validates :multiplier, numericality: {equal_to: 1}, unless: :base
|
validates :multiplier, numericality: {equal_to: 1}, unless: :base
|
||||||
validates :multiplier, numericality: {greater_than: 0, precision: true, scale: true}, if: :base
|
validates :multiplier, numericality: {greater_than: 0, precision: true, scale: true}, if: :base
|
||||||
|
|
||||||
@@ -22,23 +22,12 @@ class Unit < ApplicationRecord
|
|||||||
actionable_units = Arel::Table.new('actionable_units')
|
actionable_units = Arel::Table.new('actionable_units')
|
||||||
units = actionable_units.alias('units')
|
units = actionable_units.alias('units')
|
||||||
bases_units = arel_table.alias('bases_units')
|
bases_units = arel_table.alias('bases_units')
|
||||||
# Use 'all_units' alias for correlated subqueries to reference the all_units CTE.
|
other_units = arel_table.alias('other_units')
|
||||||
# Cannot use arel_table.alias here because the CTE is named 'all_units', not 'units'.
|
other_bases_units = arel_table.alias('other_bases_units')
|
||||||
other_units = Arel::Table.new('all_units').alias('other_units')
|
|
||||||
other_bases_units = Arel::Table.new('all_units').alias('other_bases_units')
|
|
||||||
sub_units = arel_table.alias('sub_units')
|
sub_units = arel_table.alias('sub_units')
|
||||||
|
|
||||||
Unit.with_recursive(
|
Unit.with_recursive(actionable_units: [
|
||||||
# Renamed from 'units' to 'all_units' to avoid SQLite circular reference:
|
self.or(Unit.defaults).left_joins(:base)
|
||||||
# SQLite treats any CTE in WITH RECURSIVE that references a table with the same
|
|
||||||
# name as the CTE itself as a circular reference, even for non-recursive CTEs.
|
|
||||||
all_units: self.or(Unit.defaults),
|
|
||||||
actionable_units: [
|
|
||||||
# Read from all_units CTE (user+defaults) aliased as the units table name.
|
|
||||||
# Using AR::Relation (not Arel::SelectManager) to avoid extra parentheses
|
|
||||||
# around the UNION part (visit_Arel_SelectManager always wraps in parens).
|
|
||||||
Unit.from(Arel::Table.new('all_units').as(arel_table.name))
|
|
||||||
.joins("LEFT OUTER JOIN all_units bases_units ON bases_units.id = units.base_id")
|
|
||||||
.where.not(
|
.where.not(
|
||||||
# Exclude Units that are/have default counterpart
|
# Exclude Units that are/have default counterpart
|
||||||
Arel::SelectManager.new.project(1).from(other_units)
|
Arel::SelectManager.new.project(1).from(other_units)
|
||||||
@@ -74,13 +63,15 @@ class Unit < ApplicationRecord
|
|||||||
),
|
),
|
||||||
# Fill base Units to display proper hierarchy. Duplicates will be removed
|
# Fill base Units to display proper hierarchy. Duplicates will be removed
|
||||||
# by final group() - can't be deduplicated with UNION due to 'portable' field.
|
# by final group() - can't be deduplicated with UNION due to 'portable' field.
|
||||||
# Using AR::Relation instead of Arel::SelectManager to avoid extra parentheses
|
# Use ActiveRecord::Relation (not a raw SelectManager) so the SQLite Arel
|
||||||
# around the UNION part (visit_Arel_SelectManager always wraps in parens).
|
# visitor does not wrap it in parentheses inside the UNION ALL CTE body.
|
||||||
Unit.from(Arel::Table.new('all_units').as(arel_table.name))
|
Unit.joins(
|
||||||
.joins("INNER JOIN actionable_units ON actionable_units.base_id = units.id")
|
arel_table.create_join(
|
||||||
.select(arel_table[Arel.star], Arel::Nodes.build_quoted(nil).as('portable'))
|
actionable_units,
|
||||||
]
|
arel_table.create_on(actionable_units[:base_id].eq(arel_table[:id]))
|
||||||
).select(units: [:base_id, :symbol])
|
)
|
||||||
|
).select(arel_table[Arel.star], Arel::Nodes.build_quoted(nil).as('portable'))
|
||||||
|
]).select(units: [:base_id, :symbol])
|
||||||
.select(
|
.select(
|
||||||
units[:id].minimum.as('id'), # can be ANY_VALUE()
|
units[:id].minimum.as('id'), # can be ANY_VALUE()
|
||||||
units[:user_id].minimum.as('user_id'), # prefer non-default
|
units[:user_id].minimum.as('user_id'), # prefer non-default
|
||||||
@@ -96,7 +87,7 @@ class Unit < ApplicationRecord
|
|||||||
def self.ordering
|
def self.ordering
|
||||||
[arel_table.coalesce(Arel::Table.new(:bases_units)[:symbol], arel_table[:symbol]),
|
[arel_table.coalesce(Arel::Table.new(:bases_units)[:symbol], arel_table[:symbol]),
|
||||||
arel_table[:base_id].not_eq(nil),
|
arel_table[:base_id].not_eq(nil),
|
||||||
:multiplier,
|
arel_table[:multiplier],
|
||||||
arel_table[:symbol]]
|
arel_table[:symbol]]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -58,4 +58,7 @@ Rails.application.configure do
|
|||||||
# config.action_view.annotate_rendered_view_with_filenames = true
|
# config.action_view.annotate_rendered_view_with_filenames = true
|
||||||
|
|
||||||
config.log_level = :info
|
config.log_level = :info
|
||||||
|
|
||||||
|
# Allow the default integration test host.
|
||||||
|
config.hosts << "www.example.com"
|
||||||
end
|
end
|
||||||
|
|||||||
50
lib/tasks/test_multi_db.rake
Normal file
50
lib/tasks/test_multi_db.rake
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
namespace :test do
|
||||||
|
desc "Run Rails tests against all supported database adapters (SQLite, MySQL)"
|
||||||
|
task :all_adapters do
|
||||||
|
# DATABASE_URL overrides the adapter from database.yml at runtime.
|
||||||
|
# MySQL requires the mysql2 gem: bundle install --with mysql
|
||||||
|
adapters = {
|
||||||
|
"SQLite" => {
|
||||||
|
"DATABASE_URL" => "sqlite3:db/test.sqlite3"
|
||||||
|
},
|
||||||
|
"MySQL" => {
|
||||||
|
"DATABASE_URL" => format(
|
||||||
|
"mysql2://%s:%s@%s/%s",
|
||||||
|
ENV.fetch("DATABASE_USERNAME", "root"),
|
||||||
|
ENV.fetch("DATABASE_PASSWORD", ""),
|
||||||
|
ENV.fetch("DATABASE_HOST", "127.0.0.1"),
|
||||||
|
ENV.fetch("DATABASE_NAME", "fixin_test")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
failed = []
|
||||||
|
|
||||||
|
adapters.each do |name, extra_env|
|
||||||
|
puts "\n#{"=" * 60}"
|
||||||
|
puts " Running tests with #{name}"
|
||||||
|
puts "=" * 60
|
||||||
|
|
||||||
|
env = ENV.to_h.merge("RAILS_ENV" => "test").merge(extra_env)
|
||||||
|
|
||||||
|
# Reset test database; db:drop may fail on first run — that's fine
|
||||||
|
system(env, "bin/rails db:drop")
|
||||||
|
unless system(env, "bin/rails db:create db:schema:load")
|
||||||
|
failed << "#{name} (database setup)"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
failed << name unless system(env, "bin/rails test")
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "\n#{"=" * 60}"
|
||||||
|
if failed.any?
|
||||||
|
puts " FAILED: #{failed.join(", ")}"
|
||||||
|
puts "=" * 60
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
puts " All adapters passed!"
|
||||||
|
puts "=" * 60
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
require "test_helper"
|
require "test_helper"
|
||||||
|
|
||||||
class Default::UnitsControllerTest < ActionDispatch::IntegrationTest
|
class Default::UnitsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
setup do
|
||||||
|
sign_in users(:alice)
|
||||||
|
end
|
||||||
|
|
||||||
test "should get index" do
|
test "should get index" do
|
||||||
get units_defaults_index_url
|
get default_units_url
|
||||||
assert_response :success
|
assert_response :success
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ require "test_helper"
|
|||||||
|
|
||||||
class UsersControllerTest < ActionDispatch::IntegrationTest
|
class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||||
setup do
|
setup do
|
||||||
@user = users(:one)
|
@admin = users(:admin)
|
||||||
|
@user = users(:alice)
|
||||||
|
sign_in @admin
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should get index" do
|
test "should get index" do
|
||||||
@@ -10,39 +12,25 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
|
|||||||
assert_response :success
|
assert_response :success
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should get new" do
|
|
||||||
get new_user_url
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should create user" do
|
|
||||||
assert_difference("User.count") do
|
|
||||||
post users_url, params: { user: { email: @user.email, status: @user.status } }
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_redirected_to user_url(User.last)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should show user" do
|
test "should show user" do
|
||||||
get user_url(@user)
|
get user_url(@user)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should get edit" do
|
|
||||||
get edit_user_url(@user)
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should update user" do
|
test "should update user" do
|
||||||
patch user_url(@user), params: { user: { email: @user.email, status: @user.status } }
|
patch user_url(@user), params: { user: { status: :restricted } }, as: :turbo_stream
|
||||||
assert_redirected_to user_url(@user)
|
assert_equal "restricted", @user.reload.status
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should destroy user" do
|
test "should not update self" do
|
||||||
assert_difference("User.count", -1) do
|
patch user_url(@admin), params: { user: { status: :active } }, as: :turbo_stream,
|
||||||
delete user_url(@user)
|
headers: { "HTTP_REFERER" => users_url }
|
||||||
|
assert_response :redirect
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_redirected_to users_url
|
test "should forbid non-admin" do
|
||||||
|
sign_in @user
|
||||||
|
get users_url
|
||||||
|
assert_response :forbidden
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ ENV["RAILS_ENV"] ||= "test"
|
|||||||
require_relative "../config/environment"
|
require_relative "../config/environment"
|
||||||
require "rails/test_help"
|
require "rails/test_help"
|
||||||
|
|
||||||
|
class ActionDispatch::IntegrationTest
|
||||||
|
include Devise::Test::IntegrationHelpers
|
||||||
|
end
|
||||||
|
|
||||||
class ActiveSupport::TestCase
|
class ActiveSupport::TestCase
|
||||||
# Run tests in parallel with specified workers
|
# Run tests in parallel with specified workers
|
||||||
parallelize(workers: :number_of_processors)
|
parallelize(workers: :number_of_processors)
|
||||||
|
|||||||
Reference in New Issue
Block a user