mirror of
https://github.com/jcwimer/wrestlingApp
synced 2026-03-24 17:04:43 +00:00
Added a daily recurring job to cleanup tournaments. Fixed final score fields not loading without page refresh on mat stats page and added a cypress test for it.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
- If rails isn't installed use docker: docker run -it -v $(pwd):/rails wrestlingdev-dev <rails command>
|
||||
- If the docker image doesn't exist, use the build command: docker build -t wrestlingdev-dev -f deploy/rails-dev-Dockerfile .
|
||||
- If the Gemfile changes, you need to rebuild the docker image: docker build -t wrestlingdev-dev -f deploy/rails-dev-Dockerfile.
|
||||
- Do not add unnecessary comments to the code where you remove things.
|
||||
- Do not add unnecessary comments to the code where you remove things.
|
||||
- Cypress tests are created for js tests. They can be found in cypress-tests/cypress
|
||||
- Cypress tests can be run with docker: bash cypress-tests/run-cypress-tests.sh
|
||||
36
app/jobs/tournament_cleanup_job.rb
Normal file
36
app/jobs/tournament_cleanup_job.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
class TournamentCleanupJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform
|
||||
# Remove or clean up tournaments based on age and match status
|
||||
process_old_tournaments
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_old_tournaments
|
||||
# Get all tournaments older than 1 week that have a user_id
|
||||
old_tournaments = Tournament.where('date < ? AND user_id IS NOT NULL', 1.week.ago.to_date)
|
||||
|
||||
old_tournaments.each do |tournament|
|
||||
# Check if it has any non-BYE finished matches
|
||||
has_real_matches = tournament.matches.where(finished: 1).where.not(win_type: 'BYE').exists?
|
||||
|
||||
if has_real_matches
|
||||
|
||||
# 1. Remove all school delegates
|
||||
tournament.schools.each do |school|
|
||||
school.delegates.destroy_all
|
||||
end
|
||||
|
||||
# 2. Remove all tournament delegates
|
||||
tournament.delegates.destroy_all
|
||||
|
||||
# 3. Set user_id to null
|
||||
tournament.update(user_id: nil)
|
||||
else
|
||||
tournament.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
// ############### Score field changer and form validation
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
function initializeScoreFields() {
|
||||
const winTypeSelect = document.getElementById("match_win_type");
|
||||
const winnerSelect = document.getElementById("match_winner_id");
|
||||
const submitButton = document.getElementById("update-match-btn");
|
||||
@@ -9,6 +9,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const validationAlerts = document.getElementById("validation-alerts");
|
||||
const pinTimeTip = document.getElementById("pin-time-tip");
|
||||
|
||||
// If elements don't exist, don't proceed
|
||||
if (!winTypeSelect || !dynamicScoreInput || !finalScoreField) return;
|
||||
|
||||
// Variables to persist scores across win type changes
|
||||
let storedScores = {
|
||||
winnerScore: "0",
|
||||
@@ -111,7 +114,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
function validateForm() {
|
||||
const winType = winTypeSelect.value;
|
||||
const winner = winnerSelect.value;
|
||||
const winner = winnerSelect ? winnerSelect.value : null;
|
||||
let isValid = true;
|
||||
let alertMessage = "";
|
||||
|
||||
@@ -156,57 +159,65 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
alertMessage += "Please select a winner.<br>";
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
validationAlerts.innerHTML = alertMessage;
|
||||
validationAlerts.style.display = "block";
|
||||
} else {
|
||||
validationAlerts.innerHTML = ""; // Clear alerts
|
||||
validationAlerts.style.display = "none";
|
||||
if (validationAlerts) {
|
||||
if (!isValid) {
|
||||
validationAlerts.innerHTML = alertMessage;
|
||||
validationAlerts.style.display = "block";
|
||||
} else {
|
||||
validationAlerts.innerHTML = ""; // Clear alerts
|
||||
validationAlerts.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
submitButton.disabled = !isValid;
|
||||
if (submitButton) {
|
||||
submitButton.disabled = !isValid;
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector("form").addEventListener("submit", (event) => {
|
||||
const winType = winTypeSelect.value;
|
||||
if (document.querySelector("form")) {
|
||||
document.querySelector("form").addEventListener("submit", (event) => {
|
||||
const winType = winTypeSelect.value;
|
||||
|
||||
if (winType === "Pin") {
|
||||
const minuteInput = document.getElementById("minutes");
|
||||
const secondInput = document.getElementById("seconds");
|
||||
if (winType === "Pin") {
|
||||
const minuteInput = document.getElementById("minutes");
|
||||
const secondInput = document.getElementById("seconds");
|
||||
|
||||
if (minuteInput && secondInput) {
|
||||
const minutes = minuteInput.value.padStart(2, "0");
|
||||
const seconds = secondInput.value.padStart(2, "0");
|
||||
finalScoreField.value = `${minutes}:${seconds}`;
|
||||
if (minuteInput && secondInput) {
|
||||
const minutes = minuteInput.value.padStart(2, "0");
|
||||
const seconds = secondInput.value.padStart(2, "0");
|
||||
finalScoreField.value = `${minutes}:${seconds}`;
|
||||
} else {
|
||||
finalScoreField.value = ""; // Clear if no inputs
|
||||
}
|
||||
} else if (
|
||||
winType === "Decision" ||
|
||||
winType === "Major" ||
|
||||
winType === "Tech Fall"
|
||||
) {
|
||||
const winnerScoreInput = document.getElementById("winner-score");
|
||||
const loserScoreInput = document.getElementById("loser-score");
|
||||
|
||||
if (winnerScoreInput && loserScoreInput) {
|
||||
const winnerScore = winnerScoreInput.value || "0";
|
||||
const loserScore = loserScoreInput.value || "0";
|
||||
finalScoreField.value = `${winnerScore}-${loserScore}`;
|
||||
} else {
|
||||
finalScoreField.value = ""; // Clear if no inputs
|
||||
}
|
||||
} else {
|
||||
finalScoreField.value = ""; // Clear if no inputs
|
||||
finalScoreField.value = ""; // Reset for other win types
|
||||
}
|
||||
} else if (
|
||||
winType === "Decision" ||
|
||||
winType === "Major" ||
|
||||
winType === "Tech Fall"
|
||||
) {
|
||||
const winnerScoreInput = document.getElementById("winner-score");
|
||||
const loserScoreInput = document.getElementById("loser-score");
|
||||
|
||||
if (winnerScoreInput && loserScoreInput) {
|
||||
const winnerScore = winnerScoreInput.value || "0";
|
||||
const loserScore = loserScoreInput.value || "0";
|
||||
finalScoreField.value = `${winnerScore}-${loserScore}`;
|
||||
} else {
|
||||
finalScoreField.value = ""; // Clear if no inputs
|
||||
}
|
||||
} else {
|
||||
finalScoreField.value = ""; // Reset for other win types
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
winTypeSelect.addEventListener("change", updateScoreInput);
|
||||
winnerSelect.addEventListener("change", validateForm);
|
||||
if (winnerSelect) {
|
||||
winnerSelect.addEventListener("change", validateForm);
|
||||
}
|
||||
|
||||
updateScoreInput();
|
||||
validateForm();
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to create text inputs
|
||||
function createTextInput(id, placeholder, label) {
|
||||
@@ -226,4 +237,8 @@ function createTextInput(id, placeholder, label) {
|
||||
container.appendChild(input);
|
||||
return container;
|
||||
}
|
||||
|
||||
// Initialize on both DOMContentLoaded and turbolinks:load
|
||||
document.addEventListener("DOMContentLoaded", initializeScoreFields);
|
||||
document.addEventListener("turbolinks:load", initializeScoreFields);
|
||||
</script>
|
||||
|
||||
@@ -17,4 +17,6 @@ docker build -t $1 -f ${project_dir}/deploy/rails-dev-Dockerfile \
|
||||
|
||||
docker run --rm -it -p 3000:3000 \
|
||||
-v ${project_dir}:/rails \
|
||||
-v /etc/localtime:/etc/localtime:ro \
|
||||
-v /etc/timezone:/etc/timezone:ro \
|
||||
$1 /bin/bash
|
||||
@@ -1,10 +1,11 @@
|
||||
# production:
|
||||
# periodic_cleanup:
|
||||
# class: CleanSoftDeletedRecordsJob
|
||||
# queue: background
|
||||
# args: [ 1000, { batch_size: 500 } ]
|
||||
# schedule: every hour
|
||||
# periodic_command:
|
||||
# command: "SoftDeletedRecord.due.delete_all"
|
||||
# priority: 2
|
||||
# schedule: at 5am every day
|
||||
production:
|
||||
tournament_cleanup_job:
|
||||
class: TournamentCleanupJob
|
||||
schedule: every day at 3am
|
||||
queue: default
|
||||
|
||||
development:
|
||||
tournament_cleanup_job:
|
||||
class: TournamentCleanupJob
|
||||
schedule: every day at 3am
|
||||
queue: default
|
||||
BIN
cypress-tests/.DS_Store
vendored
BIN
cypress-tests/.DS_Store
vendored
Binary file not shown.
BIN
cypress-tests/cypress/.DS_Store
vendored
BIN
cypress-tests/cypress/.DS_Store
vendored
Binary file not shown.
@@ -27,7 +27,13 @@ describe('Create a tournament', () => {
|
||||
cy.get('input[name="tournament[address]"]').type('123 Wrestling Way');
|
||||
cy.get('input[name="tournament[director]"]').type('John Doe');
|
||||
cy.get('input[name="tournament[director_email]"]').type('john.doe@example.com');
|
||||
cy.get('input[name="tournament[date]"]').type('2024-12-31');
|
||||
|
||||
// Set date to 1 month from today dynamically
|
||||
const futureDate = new Date();
|
||||
futureDate.setMonth(futureDate.getMonth() + 1);
|
||||
const formattedDate = futureDate.toISOString().split('T')[0]; // Format as YYYY-MM-DD
|
||||
cy.get('input[name="tournament[date]"]').type(formattedDate);
|
||||
|
||||
cy.get('select[name="tournament[tournament_type]"]').select('Pool to bracket');
|
||||
// cy.get('input[name="tournament[is_public]"]').check();
|
||||
|
||||
|
||||
@@ -148,55 +148,125 @@ describe('Matstats Page Functionality', () => {
|
||||
// Only attempt this test if the match_win_type element exists
|
||||
cy.get('body').then(($body) => {
|
||||
if ($body.find('#match_win_type').length) {
|
||||
// Select win type as Decision
|
||||
// 1. Test Decision win type with no winner selected
|
||||
cy.get('#match_win_type').select('Decision');
|
||||
|
||||
// Check if there are input fields visible in the form
|
||||
const hasScoreInputs = $body.find('input[type="number"]').length > 0 ||
|
||||
$body.find('input[type="text"]').length > 0;
|
||||
// Wait for dynamic fields to update
|
||||
cy.wait(300);
|
||||
|
||||
if (hasScoreInputs) {
|
||||
// Try to find score inputs using a more generic approach
|
||||
cy.get('input[type="number"], input[type="text"]').then($inputs => {
|
||||
if ($inputs.length >= 2) {
|
||||
// Use the first two inputs for winner and loser scores
|
||||
cy.wrap($inputs).first().as('winnerScore');
|
||||
cy.wrap($inputs).eq(1).as('loserScore');
|
||||
|
||||
// Try invalid input (loser score > winner score)
|
||||
cy.get('@winnerScore').clear().type('2');
|
||||
cy.get('@loserScore').clear().type('5');
|
||||
|
||||
// Should show validation error
|
||||
cy.get('#validation-alerts').should('be.visible');
|
||||
|
||||
// Update to valid scores for Decision
|
||||
cy.get('@winnerScore').clear().type('5');
|
||||
cy.get('@loserScore').clear().type('2');
|
||||
|
||||
// Error should be gone after valid input
|
||||
cy.get('#validation-alerts').should('not.exist').should('not.be.visible');
|
||||
|
||||
// Test Major validation (score difference 8+)
|
||||
cy.get('@winnerScore').clear().type('10');
|
||||
cy.get('@loserScore').clear().type('2');
|
||||
|
||||
// Should show type validation error for needing Major
|
||||
cy.get('#validation-alerts').should('be.visible');
|
||||
|
||||
// Change to Major
|
||||
cy.get('#match_win_type').select('Major');
|
||||
|
||||
// Error should be gone after changing win type
|
||||
cy.wait(500); // Give validation time to update
|
||||
cy.get('#validation-alerts').should('not.exist').should('not.be.visible');
|
||||
} else {
|
||||
cy.log('Found fewer than 2 score input fields - test conditionally passed');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cy.log('No score input fields found - test conditionally passed');
|
||||
}
|
||||
// Verify correct form fields appear
|
||||
cy.get('#winner-score').should('exist');
|
||||
cy.get('#loser-score').should('exist');
|
||||
|
||||
// Enter valid scores for Decision
|
||||
cy.get('#winner-score').clear().type('5');
|
||||
cy.get('#loser-score').clear().type('2');
|
||||
|
||||
// Without a winner, form should show validation error
|
||||
cy.get('#validation-alerts').should('be.visible')
|
||||
.and('contain.text', 'Please select a winner');
|
||||
cy.get('#update-match-btn').should('be.disabled');
|
||||
|
||||
// 2. Test invalid score scenario (loser score > winner score)
|
||||
cy.get('#winner-score').clear().type('2');
|
||||
cy.get('#loser-score').clear().type('5');
|
||||
|
||||
cy.get('#validation-alerts').should('be.visible')
|
||||
.and('contain.text', 'Winner\'s score must be higher than loser\'s score');
|
||||
cy.get('#update-match-btn').should('be.disabled');
|
||||
|
||||
// 3. Fix scores and select a winner
|
||||
cy.get('#winner-score').clear().type('5');
|
||||
cy.get('#loser-score').clear().type('2');
|
||||
cy.get('#match_winner_id').select(1);
|
||||
|
||||
// Now validation should pass for Decision
|
||||
cy.get('#validation-alerts').should('not.be.visible');
|
||||
cy.get('#update-match-btn').should('not.be.disabled');
|
||||
|
||||
// 4. Test Major score range validation
|
||||
cy.get('#winner-score').clear().type('10');
|
||||
cy.get('#loser-score').clear().type('2');
|
||||
// Score difference is 8, should require Major
|
||||
cy.get('#validation-alerts').should('be.visible')
|
||||
.and('contain.text', 'Win type should be');
|
||||
cy.get('#update-match-btn').should('be.disabled');
|
||||
|
||||
// 5. Fix by changing win type to Major
|
||||
cy.get('#match_win_type').select('Major');
|
||||
cy.wait(300);
|
||||
|
||||
// Validation should now pass
|
||||
cy.get('#validation-alerts').should('not.be.visible');
|
||||
cy.get('#update-match-btn').should('not.be.disabled');
|
||||
|
||||
// 6. Test Tech Fall score range validation
|
||||
cy.get('#winner-score').clear().type('17');
|
||||
cy.get('#loser-score').clear().type('2');
|
||||
// Score difference is 15, should require Tech Fall
|
||||
cy.get('#validation-alerts').should('be.visible')
|
||||
.and('contain.text', 'Win type should be');
|
||||
cy.get('#update-match-btn').should('be.disabled');
|
||||
|
||||
// 7. Fix by changing win type to Tech Fall
|
||||
cy.get('#match_win_type').select('Tech Fall');
|
||||
cy.wait(300);
|
||||
|
||||
// Validation should now pass
|
||||
cy.get('#validation-alerts').should('not.be.visible');
|
||||
cy.get('#update-match-btn').should('not.be.disabled');
|
||||
|
||||
// 8. Test Pin win type form
|
||||
cy.get('#match_win_type').select('Pin');
|
||||
cy.wait(300);
|
||||
|
||||
// Should show pin time inputs
|
||||
cy.get('#minutes').should('exist');
|
||||
cy.get('#seconds').should('exist');
|
||||
cy.get('#pin-time-tip').should('be.visible');
|
||||
|
||||
// Winner still required
|
||||
cy.get('#validation-alerts').should('not.be.visible'); // Previous winner selection should still be valid
|
||||
|
||||
// 9. Test other win types (no score input)
|
||||
cy.get('#match_win_type').select('Forfeit');
|
||||
cy.wait(300);
|
||||
|
||||
// Should not show score inputs
|
||||
cy.get('#dynamic-score-input').should('be.empty');
|
||||
|
||||
// Winner still required
|
||||
cy.get('#validation-alerts').should('not.be.visible'); // Previous winner selection should still be valid
|
||||
} else {
|
||||
cy.log('Match form not present - test conditionally passed');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should display final score fields without requiring page refresh', () => {
|
||||
// Check if we're on a mat page with match form
|
||||
cy.get('body').then(($body) => {
|
||||
if ($body.find('#match_win_type').length) {
|
||||
// Test Decision type first
|
||||
cy.get('#match_win_type').select('Decision');
|
||||
cy.wait(300);
|
||||
cy.get('#dynamic-score-input').should('exist');
|
||||
cy.get('#winner-score').should('exist');
|
||||
cy.get('#loser-score').should('exist');
|
||||
|
||||
// Test Pin type
|
||||
cy.get('#match_win_type').select('Pin');
|
||||
cy.wait(300);
|
||||
cy.get('#minutes').should('exist');
|
||||
cy.get('#seconds').should('exist');
|
||||
cy.get('#pin-time-tip').should('be.visible');
|
||||
|
||||
// Test other types
|
||||
cy.get('#match_win_type').select('Forfeit');
|
||||
cy.wait(300);
|
||||
cy.get('#dynamic-score-input').should('be.empty');
|
||||
|
||||
cy.log('Final score fields load correctly without page refresh');
|
||||
} else {
|
||||
cy.log('Match form not present - test conditionally passed');
|
||||
}
|
||||
|
||||
@@ -161,6 +161,9 @@ describe('Matstats Real-time Updates', () => {
|
||||
cy.get('#cable-status-indicator', { timeout: 10000 })
|
||||
.should('contain.text', 'Connected');
|
||||
|
||||
// Select a winner
|
||||
cy.get('#match_winner_id').select(1);
|
||||
|
||||
// Check if match form and inputs still exist after reload
|
||||
cy.get('body').then(($reloadedBody) => {
|
||||
if ($reloadedBody.find('#match_win_type').length &&
|
||||
|
||||
13
db/seeds.rb
13
db/seeds.rb
@@ -30,8 +30,11 @@
|
||||
|
||||
User.create(id: 1, email: 'test@test.com', password: 'password', password_confirmation: 'password')
|
||||
|
||||
# Set tournament date to a month from today
|
||||
future_date = 1.month.from_now.to_date
|
||||
|
||||
# Pool to bracket
|
||||
tournament = Tournament.create(id: 200, name: 'Pool to bracket', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Pool to bracket', user_id: 1, date: Date.today, is_public: true)
|
||||
tournament = Tournament.create(id: 200, name: 'Pool to bracket', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Pool to bracket', user_id: 1, date: future_date, is_public: true)
|
||||
create_schools(tournament, 24)
|
||||
weight_classes=Weight::HS_WEIGHT_CLASSES.split(",")
|
||||
tournament.create_pre_defined_weights(weight_classes)
|
||||
@@ -58,7 +61,7 @@
|
||||
end
|
||||
|
||||
# Modified 16 Man Double Elimination 1-6
|
||||
tournament = Tournament.create(id: 201, name: 'Modified 16 Man Double Elimination 1-6', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Modified 16 Man Double Elimination 1-6', user_id: 1, date: Date.today, is_public: true)
|
||||
tournament = Tournament.create(id: 201, name: 'Modified 16 Man Double Elimination 1-6', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Modified 16 Man Double Elimination 1-6', user_id: 1, date: future_date, is_public: true)
|
||||
create_schools(tournament, 16)
|
||||
weight_classes=Weight::HS_WEIGHT_CLASSES.split(",")
|
||||
tournament.create_pre_defined_weights(weight_classes)
|
||||
@@ -69,7 +72,7 @@
|
||||
end
|
||||
|
||||
# Modified 16 Man Double Elimination 1-8
|
||||
tournament = Tournament.create(id: 202, name: 'Modified 16 Man Double Elimination 1-8', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Modified 16 Man Double Elimination 1-8', user_id: 1, date: Date.today, is_public: true)
|
||||
tournament = Tournament.create(id: 202, name: 'Modified 16 Man Double Elimination 1-8', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Modified 16 Man Double Elimination 1-8', user_id: 1, date: future_date, is_public: true)
|
||||
create_schools(tournament, 16)
|
||||
weight_classes=Weight::HS_WEIGHT_CLASSES.split(",")
|
||||
tournament.create_pre_defined_weights(weight_classes)
|
||||
@@ -86,7 +89,7 @@
|
||||
end
|
||||
|
||||
# Regular Double Elimination 1-6
|
||||
tournament = Tournament.create(id: 203, name: 'Regular Double Elimination 1-6', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Regular Double Elimination 1-6', user_id: 1, date: Date.today, is_public: true)
|
||||
tournament = Tournament.create(id: 203, name: 'Regular Double Elimination 1-6', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Regular Double Elimination 1-6', user_id: 1, date: future_date, is_public: true)
|
||||
create_schools(tournament, 32)
|
||||
weight_classes=Weight::HS_WEIGHT_CLASSES.split(",")
|
||||
tournament.create_pre_defined_weights(weight_classes)
|
||||
@@ -109,7 +112,7 @@
|
||||
end
|
||||
|
||||
# Regular Double Elimination 1-8
|
||||
tournament = Tournament.create(id: 204, name: 'Regular Double Elimination 1-8', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Regular Double Elimination 1-8', user_id: 1, date: Date.today, is_public: true)
|
||||
tournament = Tournament.create(id: 204, name: 'Regular Double Elimination 1-8', address: 'some place', director: 'some guy', director_email: 'their@email.com', tournament_type: 'Regular Double Elimination 1-8', user_id: 1, date: future_date, is_public: true)
|
||||
create_schools(tournament, 32)
|
||||
weight_classes=Weight::HS_WEIGHT_CLASSES.split(",")
|
||||
tournament.create_pre_defined_weights(weight_classes)
|
||||
|
||||
@@ -31,7 +31,7 @@ docker-compose -f ${project_dir}/deploy/docker-compose-test.yml up -d
|
||||
|
||||
# DISABLE_DATABASE_ENVIRONMENT_CHECK=1 is needed because this is "destructive" action on production
|
||||
echo Resetting the db with seed data
|
||||
docker-compose -f ${project_dir}/deploy/docker-compose-test.yml exec -T app bash -c "DISABLE_DATABASE_ENVIRONMENT_CHECK=1 rake db:reset"
|
||||
docker-compose -f ${project_dir}/deploy/docker-compose-test.yml run --rm app bash -c "DISABLE_DATABASE_ENVIRONMENT_CHECK=1 rake db:reset"
|
||||
|
||||
# echo Simulating tournament 204
|
||||
# docker-compose -f ${project_dir}/deploy/docker-compose-test.yml exec -T app rails tournament:assign_random_wins
|
||||
|
||||
@@ -36,6 +36,9 @@ services:
|
||||
- "443:443"
|
||||
healthcheck:
|
||||
test: curl http://127.0.0.1/
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
|
||||
db:
|
||||
image: mariadb:10.10
|
||||
@@ -43,6 +46,8 @@ services:
|
||||
- "3306:3306"
|
||||
volumes:
|
||||
- mysql:/var/lib/mysql
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=password
|
||||
restart: always
|
||||
@@ -65,3 +70,5 @@ services:
|
||||
metrics:
|
||||
volumes:
|
||||
- influxdb:/var/lib/influxdb
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
|
||||
11
test/fixtures/tournaments.yml
vendored
11
test/fixtures/tournaments.yml
vendored
@@ -9,4 +9,15 @@ one:
|
||||
tournament_type: Pool to bracket
|
||||
user_id: 1
|
||||
date: 2015-12-30
|
||||
is_public: true
|
||||
|
||||
two:
|
||||
id: 2
|
||||
name: Test Tournament 2
|
||||
address: Some Place
|
||||
director: Jacob Cody Wimer
|
||||
director_email: jacob.wimer@gmail.com
|
||||
tournament_type: Pool to bracket
|
||||
user_id: 1
|
||||
date: 2015-12-30
|
||||
is_public: true
|
||||
83
test/jobs/tournament_cleanup_job_test.rb
Normal file
83
test/jobs/tournament_cleanup_job_test.rb
Normal file
@@ -0,0 +1,83 @@
|
||||
require 'test_helper'
|
||||
|
||||
class TournamentCleanupJobTest < ActiveJob::TestCase
|
||||
setup do
|
||||
# Create an old empty tournament (1 week old, 0 finished matches)
|
||||
@old_empty_tournament = tournaments(:one)
|
||||
@old_empty_tournament.update(date: 2.weeks.ago.to_date)
|
||||
|
||||
# Create an old active tournament (1 week old, with finished matches)
|
||||
@old_active_tournament = tournaments(:two)
|
||||
@old_active_tournament.update(date: 2.weeks.ago.to_date)
|
||||
|
||||
# Add a finished match to the active tournament using create instead of fixtures
|
||||
weight = Weight.create(max: 120, tournament: @old_active_tournament)
|
||||
wrestler = Wrestler.create(name: "Test Wrestler", weight: weight, school: schools(:one))
|
||||
|
||||
@match = Match.create(
|
||||
tournament: @old_active_tournament,
|
||||
weight: weight,
|
||||
bracket_position: "Pool",
|
||||
round: 1,
|
||||
finished: 1,
|
||||
win_type: "Decision",
|
||||
score: "10-5",
|
||||
w1: wrestler.id,
|
||||
winner_id: wrestler.id
|
||||
)
|
||||
|
||||
# Add delegates to test removal
|
||||
tournament_delegate = TournamentDelegate.create(tournament: @old_active_tournament, user: users(:one))
|
||||
school = schools(:one)
|
||||
school.update(tournament_id: @old_active_tournament.id)
|
||||
school_delegate = SchoolDelegate.create(school: school, user: users(:one))
|
||||
end
|
||||
|
||||
test "removes old empty tournaments" do
|
||||
# In this test, only the empty tournament should be deleted
|
||||
@match.update!(win_type: "Decision") # Ensure this tournament has a non-BYE match
|
||||
|
||||
assert_difference 'Tournament.count', -1 do
|
||||
TournamentCleanupJob.perform_now
|
||||
end
|
||||
|
||||
assert_raises(ActiveRecord::RecordNotFound) { @old_empty_tournament.reload }
|
||||
assert_nothing_raised { @old_active_tournament.reload }
|
||||
end
|
||||
|
||||
test "removes old empty tournaments with only a bye finished match" do
|
||||
# Update the win_type to BYE and score to empty as required by validation
|
||||
@match.update!(win_type: "BYE", score: "")
|
||||
assert_equal "BYE", @match.reload.win_type
|
||||
assert_equal "", @match.score
|
||||
|
||||
# Both tournaments should be deleted (the empty one and the one with only BYE matches)
|
||||
assert_difference 'Tournament.count', -2 do
|
||||
TournamentCleanupJob.perform_now
|
||||
end
|
||||
|
||||
assert_raises(ActiveRecord::RecordNotFound) { @old_empty_tournament.reload }
|
||||
assert_raises(ActiveRecord::RecordNotFound) { @old_active_tournament.reload }
|
||||
end
|
||||
|
||||
test "cleans up old active tournaments" do
|
||||
# Ensure this tournament has a non-BYE match
|
||||
@match.update!(win_type: "Decision")
|
||||
|
||||
TournamentCleanupJob.perform_now
|
||||
|
||||
# Tournament should still exist
|
||||
@old_active_tournament.reload
|
||||
|
||||
# User association should be removed
|
||||
assert_nil @old_active_tournament.user_id
|
||||
|
||||
# Tournament delegates should be removed
|
||||
assert_equal 0, @old_active_tournament.delegates.count
|
||||
|
||||
# School delegates should be removed
|
||||
@old_active_tournament.schools.each do |school|
|
||||
assert_equal 0, school.delegates.count
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user