1- use config:: { Config , File , FileFormat } ;
21use root:: db:: leaderboard:: Leaderboard ;
32use root:: db:: member:: Member ;
43use root:: leaderboard:: fetch_stats:: { fetch_codeforces_stats, fetch_leetcode_stats} ;
54use root:: leaderboard:: update_leaderboard:: update_leaderboard;
65use sqlx:: { postgres:: PgPoolOptions , PgPool } ;
6+ use std:: env;
77use std:: sync:: Arc ;
88
99pub 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+ }
2014}
2115
2216// Helper function to create a test database connection
2317async fn setup_test_db ( ) -> PgPool {
2418 let database_url = get_database_url ( ) ;
25- PgPoolOptions :: new ( )
19+ let pool = PgPoolOptions :: new ( )
2620 . max_connections ( 5 )
2721 . connect ( & database_url)
2822 . 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
3083}
3184
3285// Helper function to clean up test data
86+
3387async 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" ) ;
50133}
51134
52135//test
53136#[ tokio:: test]
54137async fn test_insert_members_and_update_stats ( ) {
55138 let pool = setup_test_db ( ) . await ;
56139
57- cleanup_test_data ( & pool) . await ;
58-
59140 // Define test members
60141 let members = vec ! [
61142 (
@@ -67,6 +148,7 @@ async fn test_insert_members_and_update_stats() {
67148 2021 ,
68149 "00:11:22:33:44:55" ,
69150 Some ( "john_discord" ) ,
151+ 1 ,
70152 "swayam-agrahari" ,
71153 "tourist" ,
72154 ) ,
@@ -79,6 +161,7 @@ async fn test_insert_members_and_update_stats() {
79161 2021 ,
80162 "66:77:88:99:AA:BB" ,
81163 Some ( "jane_discord" ) ,
164+ 2 ,
82165 "rihaan1810" ,
83166 "tourist" ,
84167 ) ,
@@ -90,8 +173,8 @@ async fn test_insert_members_and_update_stats() {
90173 for member in & members {
91174 // Insert Member
92175 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 )
95178 RETURNING *" ,
96179 )
97180 . bind ( & member. 0 )
@@ -102,6 +185,7 @@ async fn test_insert_members_and_update_stats() {
102185 . bind ( member. 5 )
103186 . bind ( & member. 6 )
104187 . bind ( & member. 7 )
188+ . bind ( & member. 8 )
105189 . fetch_one ( & pool)
106190 . await
107191 . expect ( "Failed to insert member" ) ;
@@ -112,7 +196,7 @@ async fn test_insert_members_and_update_stats() {
112196 VALUES ($1, $2, 0,0,0,0,0,0,0)" ,
113197 )
114198 . bind ( member_result. id )
115- . bind ( & member. 8 )
199+ . bind ( & member. 9 )
116200 . execute ( & pool)
117201 . await
118202 . expect ( "Failed to insert LeetCode stats" ) ;
@@ -123,7 +207,7 @@ async fn test_insert_members_and_update_stats() {
123207 VALUES ($1, $2, 0,0,0)" ,
124208 )
125209 . bind ( member_result. id )
126- . bind ( & member. 9 )
210+ . bind ( & member. 10 )
127211 . execute ( & pool)
128212 . await
129213 . expect ( "Failed to insert Codeforces stats" ) ;
@@ -132,7 +216,7 @@ async fn test_insert_members_and_update_stats() {
132216 }
133217
134218 // 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 ) ) {
136220 match fetch_leetcode_stats ( Arc :: new ( pool. clone ( ) ) , * member_id, leetcode_username) . await {
137221 Ok ( _) => println ! (
138222 "Successfully fetched LeetCode stats for member ID: {}" ,
@@ -155,8 +239,6 @@ async fn test_insert_members_and_update_stats() {
155239 ) ,
156240 Err ( e) => {
157241 println ! ( "Error fetching Codeforces stats: {:?}" , e) ;
158- // Uncomment to fail test on fetch error
159- // panic!("Failed to fetch Codeforces stats")
160242 }
161243 }
162244 }
@@ -192,21 +274,5 @@ async fn test_insert_members_and_update_stats() {
192274 ) ;
193275 }
194276
195- // Clean up
196277 cleanup_test_data ( & pool) . await ;
197278}
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