1
- use config:: { Config , File , FileFormat } ;
2
1
use root:: db:: leaderboard:: Leaderboard ;
3
2
use root:: db:: member:: Member ;
4
3
use root:: leaderboard:: fetch_stats:: { fetch_codeforces_stats, fetch_leetcode_stats} ;
5
4
use root:: leaderboard:: update_leaderboard:: update_leaderboard;
6
5
use sqlx:: { postgres:: PgPoolOptions , PgPool } ;
6
+ use std:: env;
7
7
use std:: sync:: Arc ;
8
8
9
9
pub fn get_database_url ( ) -> String {
10
- // Create a configuration instance to read Secrets.toml
11
- let settings = Config :: builder ( )
12
- . add_source ( File :: new ( "Secrets" , FileFormat :: Toml ) )
13
- . build ( )
14
- . expect ( "Failed to load Secrets.toml" ) ;
15
-
16
- // Retrieve the `DATABASE_URL` from the file
17
- settings
18
- . get_string ( "DATABASE_URL" )
19
- . expect ( "Missing 'DATABASE_URL' in Secrets.toml" )
10
+ match env:: var ( "TEST_DATABASE_URL" ) {
11
+ Ok ( db_url) => db_url,
12
+ Err ( _) => "postgres://localhost:5432/default_db" . to_string ( ) ,
13
+ }
20
14
}
21
15
22
16
// Helper function to create a test database connection
23
17
async fn setup_test_db ( ) -> PgPool {
24
18
let database_url = get_database_url ( ) ;
25
- PgPoolOptions :: new ( )
19
+ let pool = PgPoolOptions :: new ( )
26
20
. max_connections ( 5 )
27
21
. connect ( & database_url)
28
22
. await
29
- . expect ( "Failed to create test database pool" )
23
+ . expect ( "Failed to create test database pool" ) ;
24
+
25
+ // Create tables if they do not already exist
26
+ let queries = vec ! [
27
+ r#"
28
+ CREATE TABLE IF NOT EXISTS member (
29
+ id SERIAL PRIMARY KEY,
30
+ rollno VARCHAR(255) NOT NULL,
31
+ name VARCHAR(255) NOT NULL,
32
+ hostel VARCHAR(255) NOT NULL,
33
+ email VARCHAR(255) NOT NULL UNIQUE,
34
+ sex VARCHAR(10) NOT NULL,
35
+ year INT NOT NULL,
36
+ macaddress VARCHAR(17) NOT NULL,
37
+ discord_id VARCHAR(255),
38
+ group_id INT NOT NULL
39
+ )"# ,
40
+ r#"
41
+ CREATE TABLE IF NOT EXISTS leaderboard (
42
+ id SERIAL PRIMARY KEY,
43
+ member_id INT UNIQUE NOT NULL,
44
+ leetcode_score INT,
45
+ codeforces_score INT,
46
+ unified_score INT NOT NULL,
47
+ last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
48
+ FOREIGN KEY (member_id) REFERENCES member(id)
49
+ )"# ,
50
+ r#"
51
+ CREATE TABLE IF NOT EXISTS leetcode_stats (
52
+ id SERIAL PRIMARY KEY,
53
+ member_id INT UNIQUE NOT NULL,
54
+ leetcode_username VARCHAR(255) NOT NULL,
55
+ problems_solved INT NOT NULL,
56
+ easy_solved INT NOT NULL,
57
+ medium_solved INT NOT NULL,
58
+ hard_solved INT NOT NULL,
59
+ contests_participated INT NOT NULL,
60
+ best_rank INT NOT NULL,
61
+ total_contests INT NOT NULL,
62
+ FOREIGN KEY (member_id) REFERENCES member(id)
63
+ )"# ,
64
+ r#"
65
+ CREATE TABLE IF NOT EXISTS codeforces_stats (
66
+ id SERIAL PRIMARY KEY,
67
+ member_id INT UNIQUE NOT NULL,
68
+ codeforces_handle VARCHAR(255) NOT NULL,
69
+ codeforces_rating INT NOT NULL,
70
+ max_rating INT NOT NULL,
71
+ contests_participated INT NOT NULL,
72
+ FOREIGN KEY (member_id) REFERENCES member(id)
73
+ )"# ,
74
+ ] ;
75
+
76
+ for query in queries {
77
+ sqlx:: query ( query)
78
+ . execute ( & pool)
79
+ . await
80
+ . expect ( "Failed to execute query" ) ;
81
+ }
82
+ pool
30
83
}
31
84
32
85
// Helper function to clean up test data
86
+
33
87
async fn cleanup_test_data ( pool : & PgPool ) {
34
- // sqlx::query("DELETE FROM leaderboard")
35
- // .execute(pool)
36
- // .await
37
- // .unwrap();
38
- // sqlx::query("DELETE FROM leetcode_stats")
39
- // .execute(pool)
40
- // .await
41
- // .unwrap();
42
- // sqlx::query("DELETE FROM codeforces_stats")
43
- // .execute(pool)
44
- // .await
45
- // .unwrap();
46
- // sqlx::query("DELETE FROM Member")
47
- // .execute(pool)
48
- // .await
49
- // .unwrap();
88
+ print ! ( "called" ) ;
89
+ let cleanup_query = r#"
90
+ DO $$
91
+ DECLARE
92
+ seq RECORD;
93
+ BEGIN
94
+ -- Droppign all the tables for cleanup purpose
95
+ BEGIN
96
+ TRUNCATE TABLE leaderboard, leetcode_stats, codeforces_stats, member RESTART IDENTITY CASCADE;
97
+ EXCEPTION
98
+ WHEN undefined_table THEN
99
+ -- Ignore errors if tables don't exist
100
+ RAISE NOTICE 'Tables do not exist, skipping TRUNCATE.';
101
+ END;
102
+
103
+ -- Postgres stores the sequences of unique id outside of respective tables, so need to delete those too.
104
+ FOR seq IN
105
+ SELECT c.relname
106
+ FROM pg_class c
107
+ JOIN pg_namespace n ON n.oid = c.relnamespace
108
+ WHERE c.relkind = 'S' AND n.nspname = 'public'
109
+ LOOP
110
+ BEGIN
111
+ EXECUTE 'ALTER SEQUENCE ' || seq.relname || ' RESTART WITH 1';
112
+ EXCEPTION
113
+ WHEN undefined_object THEN
114
+ -- Ignore errors if sequences don't exist
115
+ RAISE NOTICE 'Sequence % does not exist, skipping.', seq.relname;
116
+ END;
117
+ END LOOP;
118
+ END $$;
119
+ "# ;
120
+
121
+ sqlx:: query ( cleanup_query)
122
+ . execute ( pool)
123
+ . await
124
+ . expect ( "Failed to clean up and reset database state" ) ;
125
+ }
126
+
127
+ #[ tokio:: test]
128
+ // Additional helper test to verify database connections and basic operations
129
+ async fn test_database_connection ( ) {
130
+ let database_url = get_database_url ( ) ;
131
+ println ! ( "Database URL: {}" , database_url) ;
132
+ assert ! ( !database_url. is_empty( ) , "Database URL should not be empty" ) ;
50
133
}
51
134
52
135
//test
53
136
#[ tokio:: test]
54
137
async fn test_insert_members_and_update_stats ( ) {
55
138
let pool = setup_test_db ( ) . await ;
56
139
57
- cleanup_test_data ( & pool) . await ;
58
-
59
140
// Define test members
60
141
let members = vec ! [
61
142
(
@@ -67,6 +148,7 @@ async fn test_insert_members_and_update_stats() {
67
148
2021 ,
68
149
"00:11:22:33:44:55" ,
69
150
Some ( "john_discord" ) ,
151
+ 1 ,
70
152
"swayam-agrahari" ,
71
153
"tourist" ,
72
154
) ,
@@ -79,6 +161,7 @@ async fn test_insert_members_and_update_stats() {
79
161
2021 ,
80
162
"66:77:88:99:AA:BB" ,
81
163
Some ( "jane_discord" ) ,
164
+ 2 ,
82
165
"rihaan1810" ,
83
166
"tourist" ,
84
167
) ,
@@ -90,8 +173,8 @@ async fn test_insert_members_and_update_stats() {
90
173
for member in & members {
91
174
// Insert Member
92
175
let member_result = sqlx:: query_as :: < _ , Member > (
93
- "INSERT INTO Member (rollno, name, hostel, email, sex, year, macaddress, discord_id)
94
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
176
+ "INSERT INTO member (rollno, name, hostel, email, sex, year, macaddress, discord_id, group_id )
177
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9 )
95
178
RETURNING *" ,
96
179
)
97
180
. bind ( & member. 0 )
@@ -102,6 +185,7 @@ async fn test_insert_members_and_update_stats() {
102
185
. bind ( member. 5 )
103
186
. bind ( & member. 6 )
104
187
. bind ( & member. 7 )
188
+ . bind ( & member. 8 )
105
189
. fetch_one ( & pool)
106
190
. await
107
191
. expect ( "Failed to insert member" ) ;
@@ -112,7 +196,7 @@ async fn test_insert_members_and_update_stats() {
112
196
VALUES ($1, $2, 0,0,0,0,0,0,0)" ,
113
197
)
114
198
. bind ( member_result. id )
115
- . bind ( & member. 8 )
199
+ . bind ( & member. 9 )
116
200
. execute ( & pool)
117
201
. await
118
202
. expect ( "Failed to insert LeetCode stats" ) ;
@@ -123,7 +207,7 @@ async fn test_insert_members_and_update_stats() {
123
207
VALUES ($1, $2, 0,0,0)" ,
124
208
)
125
209
. bind ( member_result. id )
126
- . bind ( & member. 9 )
210
+ . bind ( & member. 10 )
127
211
. execute ( & pool)
128
212
. await
129
213
. expect ( "Failed to insert Codeforces stats" ) ;
@@ -132,7 +216,7 @@ async fn test_insert_members_and_update_stats() {
132
216
}
133
217
134
218
// Test LeetCode stats fetching
135
- for ( member_id, leetcode_username) in inserted_members. iter ( ) . zip ( members. iter ( ) . map ( |m| m. 8 ) ) {
219
+ for ( member_id, leetcode_username) in inserted_members. iter ( ) . zip ( members. iter ( ) . map ( |m| m. 9 ) ) {
136
220
match fetch_leetcode_stats ( Arc :: new ( pool. clone ( ) ) , * member_id, leetcode_username) . await {
137
221
Ok ( _) => println ! (
138
222
"Successfully fetched LeetCode stats for member ID: {}" ,
@@ -155,8 +239,6 @@ async fn test_insert_members_and_update_stats() {
155
239
) ,
156
240
Err ( e) => {
157
241
println ! ( "Error fetching Codeforces stats: {:?}" , e) ;
158
- // Uncomment to fail test on fetch error
159
- // panic!("Failed to fetch Codeforces stats")
160
242
}
161
243
}
162
244
}
@@ -192,21 +274,5 @@ async fn test_insert_members_and_update_stats() {
192
274
) ;
193
275
}
194
276
195
- // Clean up
196
277
cleanup_test_data ( & pool) . await ;
197
278
}
198
-
199
- // Additional helper test to verify database connections and basic operations
200
- #[ tokio:: test]
201
- async fn test_database_connection ( ) {
202
- let pool = setup_test_db ( ) . await ;
203
- let database_url = get_database_url ( ) ;
204
-
205
- // Print the URL to verify (optional, for debugging purposes)
206
- println ! ( "Database URL: {}" , database_url) ;
207
-
208
- // Basic database connectivity test
209
- let result = sqlx:: query ( "SELECT 1" ) . fetch_one ( & pool) . await ;
210
-
211
- assert ! ( result. is_ok( ) , "Database connection and query should work" ) ;
212
- }
0 commit comments