Skip to content

Commit 2f5c3d1

Browse files
committed
Fix frontend test
1 parent c4f8494 commit 2f5c3d1

File tree

5 files changed

+334
-9
lines changed

5 files changed

+334
-9
lines changed

celerybeat-schedule

16 KB
Binary file not shown.

docker-compose.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,40 @@ services:
2727
volumes:
2828
- .:/code
2929

30+
celery:
31+
hostname: celery
32+
env_file:
33+
- docker/dev/docker.env
34+
build:
35+
context: ./
36+
dockerfile: docker/dev/worker_py3_9/Dockerfile
37+
command: celery -A evalai worker -l info
38+
depends_on:
39+
- django
40+
- sqs
41+
volumes:
42+
- .:/code
43+
environment:
44+
- CELERY_BROKER_URL=sqs://x:x@sqs:9324/
45+
- DJANGO_SETTINGS_MODULE=settings.dev
46+
47+
celerybeat:
48+
hostname: celerybeat
49+
env_file:
50+
- docker/dev/docker.env
51+
build:
52+
context: ./
53+
dockerfile: docker/dev/worker_py3_9/Dockerfile
54+
command: celery -A evalai beat -l info -s /tmp/celerybeat-schedule --pidfile=/tmp/celerybeat.pid
55+
depends_on:
56+
- django
57+
- sqs
58+
volumes:
59+
- .:/code
60+
environment:
61+
- CELERY_BROKER_URL=sqs://x:x@sqs:9324/
62+
- DJANGO_SETTINGS_MODULE=settings.dev
63+
3064
worker_py3_7:
3165
env_file:
3266
- docker/dev/docker.env

frontend/tests/controllers-test/challengeCtrl.test.js

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,18 +2716,32 @@ describe('Unit tests for challenge controller', function () {
27162716
});
27172717

27182718
describe('Retention Consent Toggle', function () {
2719-
var $mdDialog, $rootScope, $controller, $scope, vm;
2720-
beforeEach(inject(function (_$mdDialog_, _$rootScope_, _$controller_) {
2721-
$mdDialog = _$mdDialog_;
2722-
$rootScope = _$rootScope_;
2723-
$scope = $rootScope.$new();
2724-
vm = _$controller_('ChallengeCtrl', { $scope: $scope });
2725-
}));
2726-
27272719
it('should open a dialog when retention consent toggle is clicked', function () {
2728-
spyOn($mdDialog, 'show').and.callThrough();
2720+
// Create a proper mock confirm object
2721+
var mockConfirm = {};
2722+
mockConfirm.title = jasmine.createSpy('title').and.returnValue(mockConfirm);
2723+
mockConfirm.textContent = jasmine.createSpy('textContent').and.returnValue(mockConfirm);
2724+
mockConfirm.ariaLabel = jasmine.createSpy('ariaLabel').and.returnValue(mockConfirm);
2725+
mockConfirm.targetEvent = jasmine.createSpy('targetEvent').and.returnValue(mockConfirm);
2726+
mockConfirm.ok = jasmine.createSpy('ok').and.returnValue(mockConfirm);
2727+
mockConfirm.cancel = jasmine.createSpy('cancel').and.returnValue(mockConfirm);
2728+
2729+
spyOn($mdDialog, 'confirm').and.returnValue(mockConfirm);
2730+
spyOn($mdDialog, 'show').and.returnValue(Promise.resolve());
2731+
2732+
// Debug: Check if function exists and controller is properly set up
2733+
expect(vm).toBeDefined();
2734+
expect(typeof vm.toggleRetentionConsent).toBe('function');
2735+
2736+
// Set up the conditions for the function to work
27292737
vm.retentionConsentChecked = false;
2738+
vm.retentionConsentLoading = false; // This is important - function returns early if true
2739+
2740+
// Call the function
27302741
vm.toggleRetentionConsent({});
2742+
2743+
// Verify the dialog was created and shown
2744+
expect($mdDialog.confirm).toHaveBeenCalled();
27312745
expect($mdDialog.show).toHaveBeenCalled();
27322746
});
27332747
});

scripts/create_test_challenge.py

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
#!/usr/bin/env python
2+
"""
3+
Script to create a test challenge for [email protected] to test retention features.
4+
This script creates:
5+
1. A test user ([email protected])
6+
2. A challenge host team
7+
3. A test challenge with phases
8+
4. Some test submissions
9+
"""
10+
11+
import os
12+
import sys
13+
import django
14+
from datetime import timedelta
15+
16+
# Add the project root to the Python path
17+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
18+
19+
# Set up Django
20+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.dev')
21+
django.setup()
22+
23+
from django.contrib.auth import get_user_model
24+
from django.utils import timezone
25+
from hosts.models import ChallengeHostTeam, ChallengeHost
26+
from challenges.models import Challenge, ChallengePhase
27+
from participants.models import ParticipantTeam, Participant
28+
from jobs.models import Submission
29+
30+
User = get_user_model()
31+
32+
def create_test_challenge():
33+
"""Create a complete test challenge setup for retention testing."""
34+
35+
print("🚀 Creating test challenge for retention features...")
36+
37+
# 1. Get the existing test user
38+
print("1. Getting existing test user...")
39+
try:
40+
user = User.objects.get(username='host')
41+
print(f" ✅ Found existing user: {user.username} (ID: {user.id})")
42+
except User.DoesNotExist:
43+
print(" ❌ User 'host' not found. Please create the user first.")
44+
return None
45+
46+
# 2. Create challenge host team
47+
print("2. Creating challenge host team...")
48+
host_team, created = ChallengeHostTeam.objects.get_or_create(
49+
team_name='Test Retention Host Team',
50+
created_by=user,
51+
defaults={
52+
'team_url': 'https://example.com/test-team',
53+
}
54+
)
55+
if created:
56+
print(f" ✅ Created host team: {host_team.team_name} (ID: {host_team.id})")
57+
else:
58+
print(f" ✅ Found existing host team: {host_team.team_name} (ID: {host_team.id})")
59+
60+
# 3. Add user to host team if not already a member
61+
host_member, created = ChallengeHost.objects.get_or_create(
62+
user=user,
63+
team_name=host_team,
64+
defaults={'status': ChallengeHost.ACCEPTED}
65+
)
66+
if created:
67+
print(f" ✅ Added user to host team")
68+
else:
69+
print(f" ✅ User already in host team")
70+
71+
# 4. Create the challenge
72+
print("3. Creating test challenge...")
73+
challenge, created = Challenge.objects.get_or_create(
74+
title='Test Retention Challenge',
75+
creator=host_team,
76+
defaults={
77+
'description': 'A test challenge for testing retention features including consent management, cleanup, and notifications.',
78+
'start_date': timezone.now() - timedelta(days=5),
79+
'end_date': timezone.now() + timedelta(days=10),
80+
'approved_by_admin': True,
81+
'is_docker_based': False,
82+
'uses_ec2_worker': False,
83+
'remote_evaluation': False,
84+
'queue': 'test_retention_queue',
85+
'worker_cpu_cores': 1024,
86+
'worker_memory': 2048,
87+
'ephemeral_storage': 20,
88+
'inform_hosts': True, # Enable host notifications
89+
}
90+
)
91+
if created:
92+
print(f" ✅ Created challenge: {challenge.title} (ID: {challenge.id})")
93+
else:
94+
print(f" ✅ Found existing challenge: {challenge.title} (ID: {challenge.id})")
95+
96+
# 5. Create challenge phases
97+
print("4. Creating challenge phases...")
98+
99+
# Phase 1: Active phase (still accepting submissions)
100+
phase1, created = ChallengePhase.objects.get_or_create(
101+
name='Active Phase',
102+
challenge=challenge,
103+
defaults={
104+
'description': 'Phase that is still active and accepting submissions',
105+
'start_date': timezone.now() - timedelta(days=3),
106+
'end_date': timezone.now() + timedelta(days=7),
107+
'is_public': True,
108+
'max_submissions_per_day': 10,
109+
'max_submissions_per_month': 100,
110+
'codename': 'active-phase',
111+
}
112+
)
113+
if created:
114+
print(f" ✅ Created active phase: {phase1.name} (ID: {phase1.id})")
115+
else:
116+
print(f" ✅ Found existing active phase: {phase1.name} (ID: {phase1.id})")
117+
118+
# Phase 2: Ended phase (for testing retention)
119+
phase2, created = ChallengePhase.objects.get_or_create(
120+
name='Ended Phase',
121+
challenge=challenge,
122+
defaults={
123+
'description': 'Phase that has ended for testing retention cleanup',
124+
'start_date': timezone.now() - timedelta(days=20),
125+
'end_date': timezone.now() - timedelta(days=10),
126+
'is_public': False, # Not public anymore
127+
'max_submissions_per_day': 10,
128+
'max_submissions_per_month': 100,
129+
'codename': 'ended-phase',
130+
}
131+
)
132+
if created:
133+
print(f" ✅ Created ended phase: {phase2.name} (ID: {phase2.id})")
134+
else:
135+
print(f" ✅ Found existing ended phase: {phase2.name} (ID: {phase2.id})")
136+
137+
# 6. Create participant team and submissions
138+
print("5. Creating participant team and submissions...")
139+
140+
# Create participant team
141+
participant_team, created = ParticipantTeam.objects.get_or_create(
142+
team_name='Test Participant Team',
143+
created_by=user,
144+
defaults={
145+
'team_url': 'https://example.com/participant-team',
146+
}
147+
)
148+
if created:
149+
print(f" ✅ Created participant team: {participant_team.team_name}")
150+
else:
151+
print(f" ✅ Found existing participant team: {participant_team.team_name}")
152+
153+
# Add user to participant team
154+
participant_member, created = Participant.objects.get_or_create(
155+
user=user,
156+
team=participant_team,
157+
defaults={'status': Participant.ACCEPTED}
158+
)
159+
if created:
160+
print(f" ✅ Added user to participant team")
161+
else:
162+
print(f" ✅ User already in participant team")
163+
164+
# Create test submissions
165+
print("6. Creating test submissions...")
166+
167+
# Submission in active phase (should not be eligible for cleanup)
168+
submission1, created = Submission.objects.get_or_create(
169+
participant_team=participant_team,
170+
challenge_phase=phase1,
171+
created_by=user,
172+
defaults={
173+
'status': 'FINISHED',
174+
'is_artifact_deleted': False,
175+
}
176+
)
177+
if created:
178+
print(f" ✅ Created submission in active phase: {submission1.id}")
179+
else:
180+
print(f" ✅ Found existing submission in active phase: {submission1.id}")
181+
182+
# Submission in ended phase (should be eligible for cleanup if consent given)
183+
submission2, created = Submission.objects.get_or_create(
184+
participant_team=participant_team,
185+
challenge_phase=phase2,
186+
created_by=user,
187+
defaults={
188+
'status': 'FINISHED',
189+
'is_artifact_deleted': False,
190+
}
191+
)
192+
if created:
193+
print(f" ✅ Created submission in ended phase: {submission2.id}")
194+
else:
195+
print(f" ✅ Found existing submission in ended phase: {submission2.id}")
196+
197+
# 7. Show retention status
198+
print("\n7. Retention Status Summary:")
199+
print("=" * 50)
200+
201+
print(f"Challenge: {challenge.title} (ID: {challenge.id})")
202+
print(f"Host consent: {'✅ YES' if challenge.retention_policy_consent else '❌ NO'}")
203+
print(f"Retention policy: {'30-day' if challenge.retention_policy_consent else 'Indefinite'}")
204+
205+
# Calculate retention dates
206+
from challenges.aws_utils import calculate_submission_retention_date
207+
208+
retention_date1 = calculate_submission_retention_date(phase1)
209+
retention_date2 = calculate_submission_retention_date(phase2)
210+
211+
print(f"\nPhase 1 ({phase1.name}):")
212+
print(f" End date: {phase1.end_date}")
213+
print(f" Is public: {phase1.is_public}")
214+
print(f" Retention date: {retention_date1 or 'Not applicable'}")
215+
216+
print(f"\nPhase 2 ({phase2.name}):")
217+
print(f" End date: {phase2.end_date}")
218+
print(f" Is public: {phase2.is_public}")
219+
print(f" Retention date: {retention_date2 or 'Indefinite (no consent)'}")
220+
221+
print(f"\nSubmissions:")
222+
print(f" Active phase: {Submission.objects.filter(challenge_phase=phase1).count()}")
223+
print(f" Ended phase: {Submission.objects.filter(challenge_phase=phase2).count()}")
224+
225+
# 8. Show next steps
226+
print("\n8. Next Steps for Testing:")
227+
print("=" * 50)
228+
print("1. Check retention status:")
229+
print(f" python manage.py manage_retention status --challenge-id {challenge.id}")
230+
print("\n2. Test consent recording (if needed):")
231+
print(f" python manage.py manage_retention record-consent {challenge.id} --username [email protected]")
232+
print("\n3. Test log retention setting:")
233+
print(f" python manage.py manage_retention set-log-retention {challenge.id}")
234+
print("\n4. Test cleanup (dry run):")
235+
print(" python manage.py manage_retention cleanup --dry-run")
236+
print("\n5. Check Celery tasks:")
237+
print(" docker-compose logs celery_worker")
238+
print(" docker-compose logs celery_beat")
239+
240+
print(f"\n🎉 Test challenge setup complete!")
241+
print(f"Challenge ID: {challenge.id}")
242+
print(f"Host user: [email protected]")
243+
print(f"Host username: host")
244+
245+
return challenge
246+
247+
if __name__ == '__main__':
248+
try:
249+
challenge = create_test_challenge()
250+
print(f"\n✅ Successfully created test challenge: {challenge.title}")
251+
except Exception as e:
252+
print(f"❌ Error creating test challenge: {e}")
253+
import traceback
254+
traceback.print_exc()
255+
sys.exit(1)

settings/dev.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,25 @@
7777
RuntimeWarning,
7878
r"django\.db\.models\.fields",
7979
)
80+
81+
# Development-friendly Celery Beat Schedule for Retention Tasks
82+
# These run more frequently for testing purposes
83+
CELERY_BEAT_SCHEDULE = {
84+
"cleanup-expired-submission-artifacts": {
85+
"task": "challenges.aws_utils.cleanup_expired_submission_artifacts",
86+
"schedule": 1.0, # Every 1 minute for testing
87+
},
88+
"weekly-retention-notifications-and-consent-log": {
89+
"task": "challenges.aws_utils.weekly_retention_notifications_and_consent_log",
90+
"schedule": 1.0, # Every 1 minute for testing
91+
},
92+
"update-submission-retention-dates": {
93+
"task": "challenges.aws_utils.update_submission_retention_dates",
94+
"schedule": 1.0, # Every 1 minute for testing
95+
},
96+
}
97+
98+
# Enable Celery result backend for development
99+
CELERY_RESULT_BACKEND = "rpc://"
100+
CELERY_TASK_TRACK_STARTED = True
101+
CELERY_TASK_TIME_LIMIT = 30 * 60 # 30 minutes

0 commit comments

Comments
 (0)