Merge branch 'main' into single_tenant

This commit is contained in:
Adam Haris
2025-12-11 06:08:01 +01:00
committed by GitHub
155 changed files with 4251 additions and 203 deletions
@@ -37,4 +37,14 @@ class Account::JoinCodesControllerTest < ActionDispatch::IntegrationTest
delete account_join_code_path
assert_response :forbidden
end
test "update with extremely large usage_limit" do
# A number larger than bigint max (2^63 - 1 = 9223372036854775807)
huge_number = "99999999999999999999999999999999999"
put account_join_code_path, params: { account_join_code: { usage_limit: huge_number } }
assert_response :unprocessable_entity
assert_select ".txt-negative", text: /cannot be larger than the population of the planet/
end
end
+31
View File
@@ -0,0 +1,31 @@
require "test_helper"
class ApiTest < ActionDispatch::IntegrationTest
setup do
@davids_bearer_token = bearer_token_env(identity_access_tokens(:davids_api_token).token)
@jasons_bearer_token = bearer_token_env(identity_access_tokens(:jasons_api_token).token)
end
test "authenticate with valid access token" do
get boards_path(format: :json), env: @davids_bearer_token
assert_response :success
end
test "fail to authenticate with invalid access token" do
get boards_path(format: :json), env: bearer_token_env("nonsense")
assert_response :unauthorized
end
test "changing data requires a write-endowed access token" do
post boards_path(format: :json), params: { board: { name: "My new board" } }, env: @jasons_bearer_token
assert_response :unauthorized
post boards_path(format: :json), params: { board: { name: "My new board" } }, env: @davids_bearer_token
assert_response :success
end
private
def bearer_token_env(token)
{ "HTTP_AUTHORIZATION" => "Bearer #{token}" }
end
end
@@ -36,4 +36,52 @@ class Boards::ColumnsControllerTest < ActionDispatch::IntegrationTest
assert_response :success
end
end
test "index as JSON" do
board = boards(:writebook)
get board_columns_path(board), as: :json
assert_response :success
assert_equal board.columns.count, @response.parsed_body.count
end
test "show as JSON" do
column = columns(:writebook_in_progress)
get board_column_path(column.board, column), as: :json
assert_response :success
assert_equal column.id, @response.parsed_body["id"]
end
test "create as JSON" do
board = boards(:writebook)
assert_difference -> { board.columns.count }, +1 do
post board_columns_path(board), params: { column: { name: "New Column" } }, as: :json
end
assert_response :created
assert_equal board_column_path(board, Column.last, format: :json), @response.headers["Location"]
end
test "update as JSON" do
column = columns(:writebook_in_progress)
put board_column_path(column.board, column), params: { column: { name: "Updated Name" } }, as: :json
assert_response :no_content
assert_equal "Updated Name", column.reload.name
end
test "destroy as JSON" do
column = columns(:writebook_on_hold)
assert_difference -> { column.board.columns.count }, -1 do
delete board_column_path(column.board, column), as: :json
end
assert_response :no_content
end
end
@@ -190,4 +190,44 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest
assert_select "input.switch__input[name='user_ids[]'][value='#{david.id}'][disabled]"
end
end
test "index as JSON" do
get boards_path, as: :json
assert_response :success
assert_equal users(:kevin).boards.count, @response.parsed_body.count
end
test "show as JSON" do
get board_path(boards(:writebook)), as: :json
assert_response :success
assert_equal boards(:writebook).name, @response.parsed_body["name"]
end
test "create as JSON" do
assert_difference -> { Board.count }, +1 do
post boards_path, params: { board: { name: "My new board" } }, as: :json
end
assert_response :created
assert_equal board_path(Board.last, format: :json), @response.headers["Location"]
end
test "update as JSON" do
board = boards(:writebook)
put board_path(board), params: { board: { name: "Updated Name" } }, as: :json
assert_response :no_content
assert_equal "Updated Name", board.reload.name
end
test "destroy as JSON" do
board = boards(:writebook)
assert_difference -> { Board.count }, -1 do
delete board_path(board), as: :json
end
assert_response :no_content
end
end
@@ -22,6 +22,20 @@ class Cards::AssignmentsControllerTest < ActionDispatch::IntegrationTest
end
end
test "create as JSON" do
card = cards(:logo)
assert_not card.assigned_to?(users(:david))
post card_assignments_path(card), params: { assignee_id: users(:david).id }, as: :json
assert_response :no_content
assert card.reload.assigned_to?(users(:david))
post card_assignments_path(card), params: { assignee_id: users(:david).id }, as: :json
assert_response :no_content
assert_not card.reload.assigned_to?(users(:david))
end
private
def assert_meta_replaced(card)
assert_turbo_stream action: :replace, target: dom_id(card, :meta)
@@ -17,4 +17,16 @@ class Cards::BoardsControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to card
end
test "update as JSON" do
card = cards(:logo)
new_board = boards(:private)
assert_not_equal new_board, card.board
put card_board_path(card), params: { board_id: new_board.id }, as: :json
assert_response :no_content
assert_equal new_board, card.reload.board
end
end
@@ -9,7 +9,7 @@ class Cards::ClosuresControllerTest < ActionDispatch::IntegrationTest
card = cards(:logo)
assert_changes -> { card.reload.closed? }, from: false, to: true do
post card_closure_path(card)
post card_closure_path(card), as: :turbo_stream
assert_card_container_rerendered(card)
end
end
@@ -18,8 +18,30 @@ class Cards::ClosuresControllerTest < ActionDispatch::IntegrationTest
card = cards(:shipping)
assert_changes -> { card.reload.closed? }, from: true, to: false do
delete card_closure_path(card)
delete card_closure_path(card), as: :turbo_stream
assert_card_container_rerendered(card)
end
end
test "create as JSON" do
card = cards(:logo)
assert_not card.closed?
post card_closure_path(card), as: :json
assert_response :no_content
assert card.reload.closed?
end
test "destroy as JSON" do
card = cards(:shipping)
assert card.closed?
delete card_closure_path(card), as: :json
assert_response :no_content
assert_not card.reload.closed?
end
end
@@ -7,6 +7,11 @@ class Cards::Comments::ReactionsControllerTest < ActionDispatch::IntegrationTest
@card = @comment.card
end
test "index" do
get card_comment_reactions_path(@card, @comment)
assert_response :success
end
test "create" do
assert_difference -> { @comment.reactions.count }, 1 do
post card_comment_reactions_path(@comment.card, @comment, format: :turbo_stream), params: { reaction: { content: "Great work!" } }
@@ -30,4 +35,29 @@ class Cards::Comments::ReactionsControllerTest < ActionDispatch::IntegrationTest
assert_response :forbidden
end
end
test "index as JSON" do
get card_comment_reactions_path(@card, @comment), as: :json
assert_response :success
assert_equal @comment.reactions.count, @response.parsed_body.count
end
test "create as JSON" do
assert_difference -> { @comment.reactions.count }, 1 do
post card_comment_reactions_path(@card, @comment), params: { reaction: { content: "👍" } }, as: :json
end
assert_response :created
end
test "destroy as JSON" do
reaction = reactions(:david)
assert_difference -> { @comment.reactions.count }, -1 do
delete card_comment_reaction_path(@card, @comment, reaction), as: :json
end
assert_response :no_content
end
end
@@ -27,4 +27,63 @@ class Cards::CommentsControllerTest < ActionDispatch::IntegrationTest
assert_response :forbidden
end
test "index as JSON" do
card = cards(:logo)
get card_comments_path(card), as: :json
assert_response :success
assert_equal card.comments.count, @response.parsed_body.count
end
test "create as JSON" do
card = cards(:logo)
assert_difference -> { card.comments.count }, +1 do
post card_comments_path(card), params: { comment: { body: "New comment" } }, as: :json
end
assert_response :created
assert_equal card_comment_path(card, Comment.last, format: :json), @response.headers["Location"]
end
test "create as JSON with custom created_at" do
card = cards(:logo)
custom_time = Time.utc(2024, 1, 15, 10, 30, 0)
assert_difference -> { card.comments.count }, +1 do
post card_comments_path(card), params: { comment: { body: "Backdated comment", created_at: custom_time } }, as: :json
end
assert_response :created
assert_equal custom_time, Comment.last.created_at
end
test "show as JSON" do
comment = comments(:logo_agreement_kevin)
get card_comment_path(cards(:logo), comment), as: :json
assert_response :success
assert_equal comment.id, @response.parsed_body["id"]
end
test "update as JSON" do
comment = comments(:logo_agreement_kevin)
put card_comment_path(cards(:logo), comment), params: { comment: { body: "Updated comment" } }, as: :json
assert_response :success
assert_equal "Updated comment", comment.reload.body.to_plain_text
end
test "destroy as JSON" do
comment = comments(:logo_agreement_kevin)
delete card_comment_path(cards(:logo), comment), as: :json
assert_response :no_content
assert_not Comment.exists?(comment.id)
end
end
@@ -18,4 +18,26 @@ class Cards::GoldnessesControllerTest < ActionDispatch::IntegrationTest
assert_card_container_rerendered(cards(:logo))
end
end
test "create as JSON" do
card = cards(:text)
assert_not card.golden?
post card_goldness_path(card), as: :json
assert_response :no_content
assert card.reload.golden?
end
test "destroy as JSON" do
card = cards(:logo)
assert card.golden?
delete card_goldness_path(card), as: :json
assert_response :no_content
assert_not card.reload.golden?
end
end
@@ -0,0 +1,31 @@
require "test_helper"
class Cards::ImagesControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in_as :kevin
end
test "destroy" do
card = cards(:logo)
card.image.attach(io: file_fixture("moon.jpg").open, filename: "moon.jpg")
assert card.image.attached?
delete card_image_path(card)
assert_redirected_to card
assert_not card.reload.image.attached?
end
test "destroy as JSON" do
card = cards(:logo)
card.image.attach(io: file_fixture("moon.jpg").open, filename: "moon.jpg")
assert card.image.attached?
delete card_image_path(card), as: :json
assert_response :no_content
assert_not card.reload.image.attached?
end
end
@@ -9,8 +9,19 @@ class Cards::NotNowsControllerTest < ActionDispatch::IntegrationTest
card = cards(:logo)
assert_changes -> { card.reload.postponed? }, from: false, to: true do
post card_not_now_path(card)
post card_not_now_path(card), as: :turbo_stream
assert_card_container_rerendered(card)
end
end
test "create as JSON" do
card = cards(:logo)
assert_not card.postponed?
post card_not_now_path(card), as: :json
assert_response :no_content
assert card.reload.postponed?
end
end
@@ -52,4 +52,47 @@ class Cards::StepsControllerTest < ActionDispatch::IntegrationTest
assert_turbo_stream action: :replace, target: dom_id(step)
end
end
test "create as JSON" do
card = cards(:logo)
assert_difference -> { card.steps.count }, +1 do
post card_steps_path(card), params: { step: { content: "New step" } }, as: :json
end
assert_response :created
assert_equal card_step_path(card, Step.last, format: :json), @response.headers["Location"]
end
test "show as JSON" do
card = cards(:logo)
step = card.steps.create!(content: "Test step")
get card_step_path(card, step), as: :json
assert_response :success
assert_equal step.id, @response.parsed_body["id"]
assert_equal "Test step", @response.parsed_body["content"]
end
test "update as JSON" do
card = cards(:logo)
step = card.steps.create!(content: "Original")
put card_step_path(card, step), params: { step: { content: "Updated" } }, as: :json
assert_response :success
assert_equal "Updated", step.reload.content
assert_equal "Updated", @response.parsed_body["content"]
end
test "destroy as JSON" do
card = cards(:logo)
step = card.steps.create!(content: "To delete")
delete card_step_path(card, step), as: :json
assert_response :no_content
assert_not Step.exists?(step.id)
end
end
@@ -23,4 +23,26 @@ class Cards::TaggingsControllerTest < ActionDispatch::IntegrationTest
assert_turbo_stream action: :replace, target: dom_id(cards(:logo), :tags)
end
end
test "toggle tag on as JSON" do
card = cards(:logo)
assert_not card.tagged_with?(tags(:mobile))
post card_taggings_path(card), params: { tag_title: tags(:mobile).title }, as: :json
assert_response :no_content
assert card.reload.tagged_with?(tags(:mobile))
end
test "toggle tag off as JSON" do
card = cards(:logo)
assert card.tagged_with?(tags(:web))
post card_taggings_path(card), params: { tag_title: tags(:web).title }, as: :json
assert_response :no_content
assert_not card.reload.tagged_with?(tags(:web))
end
end
@@ -24,4 +24,25 @@ class Cards::TriagesControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to card
end
end
test "create as JSON" do
card = cards(:logo)
column = columns(:writebook_in_progress)
post card_triage_path(card, column_id: column.id), as: :json
assert_response :no_content
assert_equal column, card.reload.column
end
test "destroy as JSON" do
card = cards(:shipping)
assert card.column.present?
delete card_triage_path(card), as: :json
assert_response :no_content
assert_nil card.reload.column
end
end
@@ -9,7 +9,7 @@ class Cards::WatchesControllerTest < ActionDispatch::IntegrationTest
cards(:logo).unwatch_by users(:kevin)
assert_changes -> { cards(:logo).watched_by?(users(:kevin)) }, from: false, to: true do
post card_watch_path(cards(:logo))
post card_watch_path(cards(:logo)), as: :turbo_stream
end
end
@@ -17,7 +17,31 @@ class Cards::WatchesControllerTest < ActionDispatch::IntegrationTest
cards(:logo).watch_by users(:kevin)
assert_changes -> { cards(:logo).watched_by?(users(:kevin)) }, from: true, to: false do
delete card_watch_path(cards(:logo))
delete card_watch_path(cards(:logo)), as: :turbo_stream
end
end
test "create as JSON" do
card = cards(:logo)
card.unwatch_by users(:kevin)
assert_not card.watched_by?(users(:kevin))
post card_watch_path(card), as: :json
assert_response :no_content
assert card.reload.watched_by?(users(:kevin))
end
test "destroy as JSON" do
card = cards(:logo)
card.watch_by users(:kevin)
assert card.watched_by?(users(:kevin))
delete card_watch_path(card), as: :json
assert_response :no_content
assert_not card.reload.watched_by?(users(:kevin))
end
end
+56
View File
@@ -132,4 +132,60 @@ class CardsControllerTest < ActionDispatch::IntegrationTest
get card_path(card)
assert_response :success
end
test "show as JSON" do
card = cards(:logo)
card.steps.create!(content: "First step")
card.steps.create!(content: "Second step", completed: true)
get card_path(card), as: :json
assert_response :success
assert_equal card.title, @response.parsed_body["title"]
assert_equal 2, @response.parsed_body["steps"].size
end
test "create as JSON" do
assert_difference -> { Card.count }, +1 do
post board_cards_path(boards(:writebook)),
params: { card: { title: "My new card", description: "Big if true", tag_ids: [ tags(:web).id, tags(:mobile).id ] } },
as: :json
end
assert_response :created
assert_equal card_path(Card.last, format: :json), @response.headers["Location"]
card = Card.last
assert_equal "My new card", card.title
assert_equal "Big if true", card.description.to_plain_text
assert_equal [ tags(:mobile), tags(:web) ].sort, card.tags.sort
end
test "create as JSON with custom created_at" do
custom_time = Time.utc(2024, 1, 15, 10, 30, 0)
assert_difference -> { Card.count }, +1 do
post board_cards_path(boards(:writebook)),
params: { card: { title: "Backdated card", created_at: custom_time } },
as: :json
end
assert_response :created
assert_equal custom_time, Card.last.created_at
end
test "update as JSON" do
card = cards(:logo)
put card_path(card, format: :json), params: { card: { title: "Update test" } }
assert_response :success
assert_equal "Update test", card.reload.title
end
test "delete as JSON" do
card = cards(:logo)
delete card_path(card, format: :json)
assert_response :no_content
assert_not Card.exists?(card.id)
end
end
@@ -0,0 +1,30 @@
require "test_helper"
class My::AccessTokensControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in_as :kevin
end
test "create new token" do
get my_access_tokens_path
assert_response :success
get new_my_access_token_path
assert_response :success
assert_changes -> { identities(:kevin).access_tokens.count }, +1 do
post my_access_tokens_path, params: { access_token: { description: "GitHub", permission: "read" } }
follow_redirect!
assert_in_body identities(:kevin).access_tokens.last.token
end
end
test "accessing new token after reveal window redirects to index" do
assert_changes -> { identities(:kevin).access_tokens.count }, +1 do
post my_access_tokens_path, params: { access_token: { description: "GitHub", permission: "read" } }
travel_to 15.seconds.from_now
follow_redirect!
assert_equal "Token is no longer visible", flash[:alert]
end
end
end
@@ -0,0 +1,17 @@
require "test_helper"
class My::IdentitiesControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in_as :kevin
end
test "show as JSON" do
identity = identities(:kevin)
untenanted do
get my_identity_path, as: :json
assert_response :success
assert_equal identity.accounts.count, @response.parsed_body["accounts"].count
end
end
end
@@ -22,4 +22,14 @@ class Notifications::BulkReadingsControllerTest < ActionDispatch::IntegrationTes
post bulk_reading_path, params: { from_tray: true }
assert_response :ok
end
test "create as JSON" do
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: false, to: true do
assert_changes -> { notifications(:layout_commented_kevin).reload.read? }, from: false, to: true do
post bulk_reading_path, as: :json
end
end
assert_response :no_content
end
end
@@ -21,4 +21,21 @@ class Notifications::ReadingsControllerTest < ActionDispatch::IntegrationTest
assert_response :success
end
end
test "create as JSON" do
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: false, to: true do
post notification_reading_path(notifications(:logo_published_kevin)), as: :json
assert_response :no_content
end
end
test "destroy as JSON" do
notification = notifications(:logo_published_kevin)
notification.read
assert_changes -> { notification.reload.read? }, from: true, to: false do
delete notification_reading_path(notification), as: :json
assert_response :no_content
end
end
end
@@ -1,4 +1,27 @@
require "test_helper"
class NotificationsControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in_as :kevin
end
test "index as JSON" do
get notifications_path, as: :json
assert_response :success
assert_kind_of Array, @response.parsed_body
assert @response.parsed_body.any? { |n| n["id"] == notifications(:logo_published_kevin).id }
end
test "index as JSON includes notification attributes" do
get notifications_path, as: :json
notification = @response.parsed_body.find { |n| n["id"] == notifications(:logo_published_kevin).id }
assert_not_nil notification["title"]
assert_not_nil notification["body"]
assert_not_nil notification["created_at"]
assert_not_nil notification["card"]
assert_not_nil notification["creator"]
end
end
+16
View File
@@ -0,0 +1,16 @@
require "test_helper"
class TagsControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in_as :kevin
end
test "index as JSON" do
tags = users(:kevin).account.tags.alphabetically
get tags_path, as: :json
assert_response :success
assert_equal tags.count, @response.parsed_body.count
assert_equal tags.pluck(:title), @response.parsed_body.pluck("title")
end
end
+46
View File
@@ -86,4 +86,50 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
assert users(:kevin).reload.avatar.attached?
assert_equal "image/png", users(:kevin).avatar.content_type
end
test "index as JSON" do
sign_in_as :kevin
get users_path, as: :json
assert_response :success
assert_equal users(:kevin).account.users.active.count, @response.parsed_body.count
end
test "show as JSON" do
sign_in_as :kevin
get user_path(users(:david)), as: :json
assert_response :success
assert_equal users(:david).name, @response.parsed_body["name"]
end
test "update as JSON" do
sign_in_as :kevin
put user_path(users(:david)), params: { user: { name: "New David" } }, as: :json
assert_response :no_content
assert_equal "New David", users(:david).reload.name
end
test "update as JSON with invalid avatar returns errors" do
sign_in_as :kevin
svg_file = fixture_file_upload("avatar.svg", "image/svg+xml")
put user_path(users(:kevin), format: :json), params: { user: { avatar: svg_file } }
assert_response :unprocessable_entity
assert @response.parsed_body["avatar"].present?
end
test "destroy as JSON" do
sign_in_as :kevin
assert_difference -> { User.active.count }, -1 do
delete user_path(users(:david)), as: :json
end
assert_response :no_content
end
end