1
0
mirror of https://github.com/jcwimer/wrestlingApp synced 2026-03-25 01:14:43 +00:00

Added tournament backups to the database and added pages to restore and create backups

This commit is contained in:
2025-01-04 16:27:52 -05:00
parent 698576dac9
commit 49fbf6735d
17 changed files with 497 additions and 41 deletions

View File

@@ -0,0 +1,78 @@
class TournamentBackupsController < ApplicationController
before_action :set_tournament
before_action :set_tournament_backup, only: [:show, :destroy, :restore]
before_action :check_access_manage
# GET /tournament/:tournament_id/tournament_backups
def index
@tournament_backups = @tournament.tournament_backups.order(created_at: :desc)
end
# GET /tournament/:tournament_id/tournament_backups/:id
def show
end
# DELETE /tournament/:tournament_id/tournament_backups/:id
def destroy
if @tournament_backup.destroy
redirect_to tournament_tournament_backups_path(@tournament), notice: 'Backup was successfully deleted.'
else
redirect_to tournament_tournament_backups_path(@tournament), alert: 'Failed to delete the backup.'
end
end
# POST /tournament/:tournament_id/tournament_backups/create
def create
TournamentBackupService.new(@tournament, 'Manual backup').create_backup
redirect_to tournament_tournament_backups_path(@tournament), notice: 'Backup was successfully created. It will show up soon, check your background jobs for status.'
end
# POST /tournament/:tournament_id/tournament_backups/:id/restore
def restore
WrestlingdevImporter.new(@tournament, @tournament_backup).import
redirect_to tournament_path(@tournament), notice: 'Restore has successfully been submitted, please check your background jobs to see if it has finished.'
end
# POST /tournament/:tournament_id/tournament_backups/import_manual
def import_manual
import_text = params[:tournament][:import_text]
if import_text.blank?
redirect_to tournament_tournament_backups_path(@tournament), alert: 'Import text cannot be blank.'
return
end
begin
# Create a temporary backup object
backup = TournamentBackup.new(
tournament: @tournament,
backup_data: Base64.encode64(import_text),
backup_reason: 'Manual Import'
)
# Pass the backup object to the importer
WrestlingdevImporter.new(@tournament, backup).import
redirect_to tournament_path(@tournament), notice: 'Restore has successfully been submitted, please check your background jobs to see if it has finished.'
rescue JSON::ParserError => e
redirect_to tournament_tournament_backups_path(@tournament), alert: "Failed to parse JSON: #{e.message}"
rescue StandardError => e
redirect_to tournament_tournament_backups_path(@tournament), alert: "An error occurred: #{e.message}"
end
end
private
def set_tournament
@tournament = Tournament.find(params[:tournament_id])
end
def set_tournament_backup
@tournament_backup = @tournament.tournament_backups.find(params[:id])
end
def check_access_manage
authorize! :manage, @tournament
end
end

View File

@@ -1,6 +1,6 @@
class TournamentsController < ApplicationController class TournamentsController < ApplicationController
before_action :set_tournament, only: [:reset_bout_board,:calculate_team_scores, :import,:export,:bout_sheets,:swap,:weigh_in_sheet,:error,:teampointadjust,:remove_teampointadjust,:remove_school_delegate,:remove_delegate,:school_delegate,:delegate,:matches,:weigh_in,:weigh_in_weight,:create_custom_weights,:show,:edit,:update,:destroy,:up_matches,:no_matches,:team_scores,:brackets,:generate_matches,:bracket,:all_brackets] before_action :set_tournament, only: [:reset_bout_board,:calculate_team_scores,:bout_sheets,:swap,:weigh_in_sheet,:error,:teampointadjust,:remove_teampointadjust,:remove_school_delegate,:remove_delegate,:school_delegate,:delegate,:matches,:weigh_in,:weigh_in_weight,:create_custom_weights,:show,:edit,:update,:destroy,:up_matches,:no_matches,:team_scores,:brackets,:generate_matches,:bracket,:all_brackets]
before_action :check_access_manage, only: [:reset_bout_board,:calculate_team_scores, :import,:export,:swap,:weigh_in_sheet,:teampointadjust,:remove_teampointadjust,:remove_school_delegate,:school_delegate,:weigh_in,:weigh_in_weight,:create_custom_weights,:update,:edit,:generate_matches,:matches] before_action :check_access_manage, only: [:reset_bout_board,:calculate_team_scores,:swap,:weigh_in_sheet,:teampointadjust,:remove_teampointadjust,:remove_school_delegate,:school_delegate,:weigh_in,:weigh_in_weight,:create_custom_weights,:update,:edit,:generate_matches,:matches]
before_action :check_access_destroy, only: [:destroy,:delegate,:remove_delegate] before_action :check_access_destroy, only: [:destroy,:delegate,:remove_delegate]
before_action :check_tournament_errors, only: [:generate_matches] before_action :check_tournament_errors, only: [:generate_matches]
before_action :check_for_matches, only: [:up_matches,:bracket,:all_brackets] before_action :check_for_matches, only: [:up_matches,:bracket,:all_brackets]
@@ -10,10 +10,6 @@ class TournamentsController < ApplicationController
end end
def export
end
def calculate_team_scores def calculate_team_scores
respond_to do |format| respond_to do |format|
if @tournament.calculate_all_team_scores if @tournament.calculate_all_team_scores
@@ -23,16 +19,6 @@ class TournamentsController < ApplicationController
end end
end end
def import
import_text = params[:tournament][:import_text]
respond_to do |format|
if WrestlingdevImporter.new(@tournament,import_text).import
format.html { redirect_to "/tournaments/#{@tournament.id}", notice: 'Import is on-going. This will take 1-5 minutes.' }
format.json { head :no_content }
end
end
end
def swap def swap
@wrestler = Wrestler.find(params[:wrestler][:originalId]) @wrestler = Wrestler.find(params[:wrestler][:originalId])
respond_to do |format| respond_to do |format|

View File

@@ -8,6 +8,7 @@ class Tournament < ApplicationRecord
has_many :matches, dependent: :destroy has_many :matches, dependent: :destroy
has_many :delegates, class_name: "TournamentDelegate" has_many :delegates, class_name: "TournamentDelegate"
has_many :mat_assignment_rules, dependent: :destroy has_many :mat_assignment_rules, dependent: :destroy
has_many :tournament_backups, dependent: :destroy
validates :date, :name, :tournament_type, :address, :director, :director_email , presence: true validates :date, :name, :tournament_type, :address, :director, :director_email , presence: true
@@ -228,4 +229,8 @@ class Tournament < ApplicationRecord
end end
end end
def create_backup()
TournamentBackupService.new(self, "Manual backup").create_backup
end
end end

View File

@@ -0,0 +1,5 @@
class TournamentBackup < ApplicationRecord
belongs_to :tournament
validates :backup_data, presence: true
end

View File

@@ -0,0 +1,63 @@
class TournamentBackupService
def initialize(tournament, reason)
@tournament = tournament
@reason = reason
end
def create_backup
if Rails.env.production?
self.delay(:job_owner_id => @tournament.id, :job_owner_type => "Create a backup").create_backup_raw
else
self.create_backup_raw
end
end
def create_backup_raw
# Generate the JSON directly in Ruby and encode it
backup_data = Base64.encode64(generate_json.to_json)
begin
# Save the backup with encoded data
TournamentBackup.create!(tournament: @tournament, backup_data: backup_data, backup_reason: @reason)
Rails.logger.info("Backup created successfully for tournament ##{@tournament.id}")
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error("Failed to save backup: #{e.message}")
end
end
private
def generate_json
{
tournament: {
attributes: @tournament.attributes,
schools: @tournament.schools.map(&:attributes),
weights: @tournament.weights.map(&:attributes),
mats: @tournament.mats.map(&:attributes),
mat_assignment_rules: @tournament.mat_assignment_rules.map do |rule|
rule.attributes.merge(
mat: Mat.find_by(id: rule.mat_id)&.attributes.slice("name"),
weight_classes: rule.weight_classes.map do |weight_id|
Weight.find_by(id: weight_id)&.max
end
)
end,
wrestlers: @tournament.wrestlers.map do |wrestler|
wrestler.attributes.merge(
school: wrestler.school&.attributes,
weight: wrestler.weight&.attributes
)
end,
matches: @tournament.matches.sort_by(&:bout_number).map do |match|
match.attributes.merge(
w1_name: Wrestler.find_by(id: match.w1)&.name,
w2_name: Wrestler.find_by(id: match.w2)&.name,
winner_name: Wrestler.find_by(id: match.winner_id)&.name,
weight: Weight.find_by(id: match.weight_id)&.attributes,
mat: Mat.find_by(id: match.mat_id)&.attributes
)
end
}
}
end
end

View File

@@ -1,11 +1,11 @@
class WrestlingdevImporter class WrestlingdevImporter
##### Note, the json contains id's for each row in the tables as well as it's associations ##### Note, the json contains id's for each row in the tables as well as its associations
##### this ignores those ids and uses this tournament id and then looks up associations based on name ##### this ignores those ids and uses this tournament id and then looks up associations based on name
##### and this tournament id ##### and this tournament id
def initialize(tournament, import_json) def initialize(tournament, backup)
@tournament = tournament @tournament = tournament
@import_data = JSON.parse(import_json) @import_data = JSON.parse(Base64.decode64(backup.backup_data))
end end
def import def import
@@ -26,6 +26,7 @@ class WrestlingdevImporter
end end
def destroy_all def destroy_all
@tournament.mat_assignment_rules.destroy_all
@tournament.mats.destroy_all @tournament.mats.destroy_all
@tournament.matches.destroy_all @tournament.matches.destroy_all
@tournament.schools.each do |school| @tournament.schools.each do |school|
@@ -42,6 +43,8 @@ class WrestlingdevImporter
parse_mats(@import_data["tournament"]["mats"]) parse_mats(@import_data["tournament"]["mats"])
parse_wrestlers(@import_data["tournament"]["wrestlers"]) parse_wrestlers(@import_data["tournament"]["wrestlers"])
parse_matches(@import_data["tournament"]["matches"]) parse_matches(@import_data["tournament"]["matches"])
puts "Parsing mat assignment rules"
parse_mat_assignment_rules(@import_data["tournament"]["mat_assignment_rules"])
end end
def parse_tournament(attributes) def parse_tournament(attributes)
@@ -70,6 +73,34 @@ class WrestlingdevImporter
end end
end end
def parse_mat_assignment_rules(mat_assignment_rules)
mat_assignment_rules.each do |rule_attributes|
mat_name = rule_attributes.dig("mat", "name")
mat = Mat.find_by(name: mat_name, tournament_id: @tournament.id)
# Map max values of weight_classes to their new IDs
new_weight_classes = rule_attributes["weight_classes"].map do |max_value|
Weight.find_by(max: max_value, tournament_id: @tournament.id)&.id
end.compact
# Extract bracket_positions and rounds
bracket_positions = rule_attributes["bracket_positions"]
rounds = rule_attributes["rounds"]
rule_attributes.except!("id", "mat", "tournament_id", "weight_classes")
MatAssignmentRule.create(
rule_attributes.merge(
tournament_id: @tournament.id,
mat_id: mat&.id,
weight_classes: new_weight_classes,
bracket_positions: bracket_positions,
rounds: rounds
)
)
end
end
def parse_wrestlers(wrestlers) def parse_wrestlers(wrestlers)
wrestlers.each do |wrestler_attributes| wrestlers.each do |wrestler_attributes|
school = School.find_by(name: wrestler_attributes["school"]["name"], tournament_id: @tournament.id) school = School.find_by(name: wrestler_attributes["school"]["name"], tournament_id: @tournament.id)

View File

@@ -33,6 +33,7 @@
<li><%= link_to "Full Screen Bout Board" , "/tournaments/#{@tournament.id}/up_matches?print=true" , target: :_blank %></li> <li><%= link_to "Full Screen Bout Board" , "/tournaments/#{@tournament.id}/up_matches?print=true" , target: :_blank %></li>
<li><%= link_to "Deduct Team Points" , "/tournaments/#{@tournament.id}/teampointadjust" %></li> <li><%= link_to "Deduct Team Points" , "/tournaments/#{@tournament.id}/teampointadjust" %></li>
<li><%= link_to "View All Mat Assignment Rules", tournament_mat_assignment_rules_path(@tournament) %></li> <li><%= link_to "View All Mat Assignment Rules", tournament_mat_assignment_rules_path(@tournament) %></li>
<li><%= link_to 'Manage Backups', tournament_tournament_backups_path(@tournament) %></li>
<li><%= link_to "Reset Bout Board", reset_bout_board_tournament_path(@tournament), method: :post, data: { confirm: "Are you sure you want to reset the bout board?" } %></li> <li><%= link_to "Reset Bout Board", reset_bout_board_tournament_path(@tournament), method: :post, data: { confirm: "Are you sure you want to reset the bout board?" } %></li>
<% if can? :destroy, @tournament %> <% if can? :destroy, @tournament %>
<li><%= link_to "Tournament Delegation" , "/tournaments/#{@tournament.id}/delegate" %></li> <li><%= link_to "Tournament Delegation" , "/tournaments/#{@tournament.id}/delegate" %></li>

View File

@@ -0,0 +1,38 @@
<h3>Tournament Backups</h3>
<p>You can click on the timestamp to view the backup text. You can manually store this anywhere you'd like and then paste it into the manual import field below.
Doing this is risky, if the text is formatted incorrectly (like Microsoft Word changing the quotation marks), it will not import properly
and will also delete all of your current data. It's best to use the create backup function.</p>
<br>
<table class="table table-hover table-condensed">
<thead>
<tr>
<th>Backup Created At</th>
<th>Backup Reason</th>
<th><%= link_to ' Create New Backup', tournament_tournament_backups_path(@tournament), method: :post, class: 'fas fa-plus'%></th>
</tr>
</thead>
<tbody>
<% @tournament_backups.each do |backup| %>
<tr>
<td>
<%= link_to backup.created_at.strftime('%Y-%m-%d %H:%M:%S'), tournament_tournament_backup_path(@tournament, backup, print: true), target: '_blank', class: 'text-decoration-none' %>
</td>
<td><%= backup.backup_reason.presence || 'No reason provided' %></td>
<td>
<%= link_to '', restore_tournament_tournament_backup_path(@tournament, backup), method: :post, data: { confirm: "This will restore the backup from #{backup.created_at.strftime('%Y-%m-%d %H:%M:%S')}. It will delete all current data from the tournament in order to restore the backup." }, class: 'fas fa-undo-alt text-warning', title: 'Restore Backup' %>
<%= link_to '', tournament_tournament_backup_path(@tournament, backup), method: :delete, data: { confirm: 'Are you sure you want to delete this backup?' }, class: 'fas fa-trash-alt', title: 'Delete Backup' %>
</td>
</tr>
<% end %>
</tbody>
</table>
<br><br>
<h3>Import Manual Backup</h3>
<p>Paste the backup text here. Note, if this is formatted wrong, you'll need to restore a backup from above to fix it and you'll see an error in your background jobs.</p>
<%= form_for(:tournament, url: import_manual_tournament_tournament_backups_path(@tournament)) do |f| %>
<div class="field">
<%= f.label 'Import text' %><br>
<%= f.text_area :import_text, cols: "30", rows: "20" %>
</div>
<%= submit_tag "Import", class: "btn btn-success", data: { confirm: 'Are you sure? This will delete everything for the current tournament and restore it with the backup text pasted below.' } %>
<% end %>

View File

@@ -0,0 +1 @@
<%= JSON.pretty_generate(JSON.parse(Base64.decode64(@tournament_backup.backup_data))) %>

View File

@@ -1,11 +0,0 @@
<% if can? :manage, @tournament %>
<br><br>
<h3>Import Data</h3>
<%= form_for(Tournament.new, url: import_url(@tournament)) do |f| %>
<div class="field">
<%= f.label 'Import text' %><br>
<%= f.text_area :import_text, cols: "30", rows: "20" %>
</div>
<%= submit_tag "Import", :class=>"btn btn-success", data: { confirm: 'Are you sure? This will delete everything for the current tournament and restore it with the backup text pasted below.' }%>
<% end %>
<% end %>

View File

@@ -119,7 +119,7 @@
</table> </table>
<% end %> <% end %>
<% if can? :manage, @tournament %> <% if can? :manage, @tournament %>
<br> <br><br>
<h3>Background Jobs</h3> <h3>Background Jobs</h3>
<p>This is a list of queued or running background jobs. Match generation, bracket advancement, team score calculation, etc.</p> <p>This is a list of queued or running background jobs. Match generation, bracket advancement, team score calculation, etc.</p>
<table class="table table-hover table-condensed"> <table class="table table-hover table-condensed">
@@ -148,9 +148,4 @@
</tbody> </tbody>
</table> </table>
<% end %> <% end %>
<br> <br><br>
<br>
<%= render 'import_form' %>

View File

@@ -11,6 +11,11 @@ Wrestling::Application.routes.draw do
member do member do
post :reset_bout_board post :reset_bout_board
end end
resources :tournament_backups, only: [:index, :show, :destroy] do
post :create, on: :collection
post :restore, on: :member
post :import_manual, on: :collection
end
end end
resources :schools resources :schools
@@ -57,8 +62,6 @@ Wrestling::Application.routes.draw do
delete 'tournaments/:id/:teampointadjust/remove_teampointadjust' => 'tournaments#remove_teampointadjust' delete 'tournaments/:id/:teampointadjust/remove_teampointadjust' => 'tournaments#remove_teampointadjust'
get 'tournaments/:id/error' => 'tournaments#error' get 'tournaments/:id/error' => 'tournaments#error'
post "/tournaments/:id/swap" => "tournaments#swap", :as => :swap_wrestlers post "/tournaments/:id/swap" => "tournaments#swap", :as => :swap_wrestlers
get 'tournaments/:id/export' => "tournaments#export"
post "/tournaments/:id/import" => "tournaments#import", :as => :import
get "/tournaments/:id/brackets" => "tournaments#show" get "/tournaments/:id/brackets" => "tournaments#show"
put "/tournaments/:id/calculate_team_scores", :to => "tournaments#calculate_team_scores" put "/tournaments/:id/calculate_team_scores", :to => "tournaments#calculate_team_scores"

View File

@@ -0,0 +1,16 @@
class CreateTournamentBackups < ActiveRecord::Migration[7.0]
def up
create_table :tournament_backups do |t|
t.integer :tournament_id, null: false, foreign_key: true
t.text :backup_data, null: false, limit: 4294967295 # Use LONGTEXT for large backups
t.string :backup_reason
t.timestamps
end
end
def down
# Drop the table
drop_table :tournament_backups
end
end

View File

@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_10_27_203209) do ActiveRecord::Schema[7.2].define(version: 2024_12_24_132705) do
create_table "delayed_jobs", force: :cascade do |t| create_table "delayed_jobs", force: :cascade do |t|
t.integer "priority", default: 0, null: false t.integer "priority", default: 0, null: false
t.integer "attempts", default: 0, null: false t.integer "attempts", default: 0, null: false
@@ -99,6 +99,14 @@ ActiveRecord::Schema[7.1].define(version: 2024_10_27_203209) do
t.index ["wrestler_id"], name: "index_teampointadjusts_on_wrestler_id" t.index ["wrestler_id"], name: "index_teampointadjusts_on_wrestler_id"
end end
create_table "tournament_backups", force: :cascade do |t|
t.integer "tournament_id", null: false
t.text "backup_data", limit: 4294967295, null: false
t.string "backup_reason"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "tournament_delegates", force: :cascade do |t| create_table "tournament_delegates", force: :cascade do |t|
t.integer "user_id" t.integer "user_id"
t.integer "tournament_id" t.integer "tournament_id"
@@ -166,5 +174,4 @@ ActiveRecord::Schema[7.1].define(version: 2024_10_27_203209) do
t.index ["school_id"], name: "index_wrestlers_on_school_id" t.index ["school_id"], name: "index_wrestlers_on_school_id"
t.index ["weight_id"], name: "index_wrestlers_on_weight_id" t.index ["weight_id"], name: "index_wrestlers_on_weight_id"
end end
end end

View File

@@ -0,0 +1,220 @@
require "test_helper"
class TournamentBackupsControllerTest < ActionController::TestCase
include Devise::Test::ControllerHelpers
setup do
@tournament = Tournament.find(1)
TournamentBackupService.new(@tournament, 'Manual backup').create_backup
@backup = @tournament.tournament_backups.first
end
def sign_in_owner
sign_in users(:one)
end
def sign_in_non_owner
sign_in users(:two)
end
def sign_in_delegate
sign_in users(:three)
end
def sign_in_school_delegate
sign_in users(:four)
end
def success
assert_response :success
end
def redirect
assert_redirected_to '/static_pages/not_allowed'
end
# Index endpoint tests
test "logged in tournament owner can access index" do
sign_in_owner
get :index, params: { tournament_id: @tournament.id }
assert_response :success
end
test "logged in delegate can access index" do
sign_in_delegate
get :index, params: { tournament_id: @tournament.id }
assert_response :success
end
test "non-tournament owner cannot access index" do
sign_in_non_owner
get :index, params: { tournament_id: @tournament.id }
redirect
end
test "school delegate cannot access index" do
sign_in_school_delegate
get :index, params: { tournament_id: @tournament.id }
redirect
end
# Show endpoint tests
test "tournament owner can view a backup" do
sign_in_owner
get :show, params: { tournament_id: @tournament.id, id: @backup.id }
assert_response :success
end
test "delegate can view a backup" do
sign_in_delegate
get :show, params: { tournament_id: @tournament.id, id: @backup.id }
assert_response :success
end
test "non-tournament owner cannot view a backup" do
sign_in_non_owner
get :show, params: { tournament_id: @tournament.id, id: @backup.id }
redirect
end
test "school delegate cannot view a backup" do
sign_in_school_delegate
get :show, params: { tournament_id: @tournament.id, id: @backup.id }
redirect
end
# Destroy endpoint tests
test "tournament owner can delete a backup" do
sign_in_owner
assert_difference("TournamentBackup.count", -1) do
delete :destroy, params: { tournament_id: @tournament.id, id: @backup.id }
end
assert_redirected_to tournament_tournament_backups_path(@tournament)
end
test "delegate can delete a backup" do
sign_in_delegate
assert_difference("TournamentBackup.count", -1) do
delete :destroy, params: { tournament_id: @tournament.id, id: @backup.id }
end
assert_redirected_to tournament_tournament_backups_path(@tournament)
end
test "non-tournament owner cannot delete a backup" do
sign_in_non_owner
assert_no_difference("TournamentBackup.count") do
delete :destroy, params: { tournament_id: @tournament.id, id: @backup.id }
end
redirect
end
test "school delegate cannot delete a backup" do
sign_in_school_delegate
assert_no_difference("TournamentBackup.count") do
delete :destroy, params: { tournament_id: @tournament.id, id: @backup.id }
end
redirect
end
# Restore endpoint tests
test "tournament owner can restore a backup" do
sign_in_owner
post :restore, params: { tournament_id: @tournament.id, id: @backup.id }
assert_redirected_to tournament_path(@tournament)
end
test "delegate can restore a backup" do
sign_in_delegate
post :restore, params: { tournament_id: @tournament.id, id: @backup.id }
assert_redirected_to tournament_path(@tournament)
end
test "non-tournament owner cannot restore a backup" do
sign_in_non_owner
post :restore, params: { tournament_id: @tournament.id, id: @backup.id }
redirect
end
test "school delegate cannot restore a backup" do
sign_in_school_delegate
post :restore, params: { tournament_id: @tournament.id, id: @backup.id }
redirect
end
# Import manual tests
test "tournament owner can manually import a backup" do
sign_in_owner
post :import_manual, params: { tournament_id: @tournament.id, tournament: { import_text: Base64.decode64(@backup.backup_data) } }
assert_redirected_to tournament_path(@tournament)
end
test "delegate can manually import a backup" do
sign_in_delegate
post :import_manual, params: { tournament_id: @tournament.id, tournament: { import_text: Base64.decode64(@backup.backup_data) } }
assert_redirected_to tournament_path(@tournament)
end
test "non-tournament owner cannot manually import a backup" do
sign_in_non_owner
post :import_manual, params: { tournament_id: @tournament.id, tournament: { import_text: Base64.decode64(@backup.backup_data) } }
redirect
end
test "school delegate cannot manually import a backup" do
sign_in_school_delegate
post :import_manual, params: { tournament_id: @tournament.id, tournament: { import_text: Base64.decode64(@backup.backup_data) } }
redirect
end
test "index shows empty list when no backups exist" do
@tournament.tournament_backups.destroy_all
sign_in_owner
get :index, params: { tournament_id: @tournament.id }
assert_response :success
assert_select 'tbody tr', 0 # Ensure no rows are rendered in the table
end
test "show action for non-existent backup" do
sign_in_owner
assert_raises(ActiveRecord::RecordNotFound) do
get :show, params: { tournament_id: @tournament.id, id: 9999 } # Use a non-existent backup ID
end
end
test "destroy action for non-existent backup" do
sign_in_owner
assert_no_difference("TournamentBackup.count") do
assert_raises(ActiveRecord::RecordNotFound) do
delete :destroy, params: { tournament_id: @tournament.id, id: 9999 } # Use a non-existent backup ID
end
end
end
test "restore action for non-existent backup" do
sign_in_owner
assert_raises(ActiveRecord::RecordNotFound) do
post :restore, params: { tournament_id: @tournament.id, id: 9999 } # Use a non-existent backup ID
end
end
test "manual import with blank input" do
sign_in_owner
post :import_manual, params: { tournament_id: @tournament.id, tournament: { import_text: '' } }
assert_redirected_to tournament_tournament_backups_path(@tournament)
assert_equal 'Import text cannot be blank.', flash[:alert]
end
test "manual import restores associations" do
schools_count = @tournament.schools.count
wrestlers_count = @tournament.wrestlers.count
matches_count = @tournament.matches.count
sign_in_owner
valid_backup_data = Base64.decode64(@backup.backup_data)
post :import_manual, params: { tournament_id: @tournament.id, tournament: { import_text: valid_backup_data } }
@tournament.reload
assert_equal schools_count, @tournament.schools.count
assert_equal wrestlers_count, @tournament.wrestlers.count
assert_equal matches_count, @tournament.matches.count
end
end

11
test/fixtures/tournament_backups.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
# This model initially had no columns defined. If you add columns to the
# model remove the "{}" from the fixture names and add the columns immediately
# below each fixture, per the syntax in the comments below
#
# one: {}
# column: value
#
# two: {}
# column: value

View File

@@ -0,0 +1,7 @@
require "test_helper"
class TournamentBackupTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end