@@ -26,16 +26,23 @@ impl Database {
2626 }
2727
2828 pub fn with_connection ( conn : Connection ) -> Result < Self > {
29+ Self :: with_connection_internal ( conn, true )
30+ }
31+
32+ fn with_connection_internal ( conn : Connection , should_seed : bool ) -> Result < Self > {
2933 let db = Database {
3034 conn : Mutex :: new ( conn) ,
3135 } ;
3236 db. init_tables ( ) ?;
37+ if should_seed {
38+ db. seed_database ( ) ?;
39+ }
3340 Ok ( db)
3441 }
3542
3643 pub fn in_memory ( ) -> Result < Self > {
3744 let conn = Connection :: open_in_memory ( ) ?;
38- Self :: with_connection ( conn)
45+ Self :: with_connection_internal ( conn, false )
3946 }
4047
4148 fn init_tables ( & self ) -> Result < ( ) > {
@@ -55,6 +62,50 @@ impl Database {
5562 Ok ( ( ) )
5663 }
5764
65+ fn seed_database ( & self ) -> Result < ( ) > {
66+ let conn = self . conn . lock ( ) . unwrap ( ) ;
67+
68+ // Check if database is empty (first launch)
69+ let count: i64 = conn. query_row (
70+ "SELECT COUNT(*) FROM lunch_list" ,
71+ [ ] ,
72+ |row| row. get ( 0 ) ,
73+ ) ?;
74+
75+ if count > 0 {
76+ // Database already has data, skip seeding
77+ return Ok ( ( ) ) ;
78+ }
79+
80+ // Parse embedded CSV and seed database
81+ let csv_data = include_str ! ( "../lunch_list.csv" ) ;
82+ let mut reader = csv:: Reader :: from_reader ( csv_data. as_bytes ( ) ) ;
83+
84+ for result in reader. records ( ) {
85+ let record = result. map_err ( |e| {
86+ rusqlite:: Error :: FromSqlConversionFailure (
87+ 0 ,
88+ rusqlite:: types:: Type :: Text ,
89+ Box :: new ( e) ,
90+ )
91+ } ) ?;
92+
93+ if record. len ( ) >= 2 {
94+ let name = record. get ( 0 ) . unwrap_or ( "" ) ;
95+ let category = record. get ( 1 ) . unwrap_or ( "" ) ;
96+
97+ if !name. is_empty ( ) && !category. is_empty ( ) {
98+ conn. execute (
99+ "INSERT INTO lunch_list (restaurants, option) VALUES (?, ?)" ,
100+ [ name, category] ,
101+ ) ?;
102+ }
103+ }
104+ }
105+
106+ Ok ( ( ) )
107+ }
108+
58109 pub fn list_all ( & self ) -> Result < Vec < Restaurant > > {
59110 let conn = self . conn . lock ( ) . unwrap ( ) ;
60111 let mut stmt = conn. prepare ( "SELECT restaurants, option FROM lunch_list ORDER BY restaurants" ) ?;
@@ -164,3 +215,59 @@ fn get_db_path() -> PathBuf {
164215 . join ( "lunch.db" )
165216 }
166217}
218+
219+ #[ cfg( test) ]
220+ mod tests {
221+ use super :: * ;
222+
223+ #[ test]
224+ fn test_seed_database_on_first_launch ( ) {
225+ let db = Database :: in_memory ( ) . unwrap ( ) ;
226+
227+ // Explicitly seed the database
228+ db. seed_database ( ) . unwrap ( ) ;
229+
230+ // Verify restaurants were seeded
231+ let restaurants = db. list_all ( ) . unwrap ( ) ;
232+ assert ! ( restaurants. len( ) > 0 , "Database should be seeded with restaurants" ) ;
233+
234+ // Verify both cheap and normal categories exist
235+ let cheap = db. list_by_category ( "cheap" ) . unwrap ( ) ;
236+ let normal = db. list_by_category ( "normal" ) . unwrap ( ) ;
237+ assert ! ( cheap. len( ) > 0 , "Should have cheap restaurants" ) ;
238+ assert ! ( normal. len( ) > 0 , "Should have normal restaurants" ) ;
239+
240+ // Verify specific restaurants from CSV
241+ assert ! (
242+ restaurants. iter( ) . any( |r| r. name == "Arbys" ) ,
243+ "Should contain Arbys from seed data"
244+ ) ;
245+ assert ! (
246+ restaurants. iter( ) . any( |r| r. name == "Bubba's" ) ,
247+ "Should contain Bubba's from seed data"
248+ ) ;
249+ }
250+
251+ #[ test]
252+ fn test_seed_database_does_not_overwrite ( ) {
253+ let db = Database :: in_memory ( ) . unwrap ( ) ;
254+
255+ // Add a custom restaurant before seeding
256+ db. add ( "Custom Restaurant" , "normal" ) . unwrap ( ) ;
257+
258+ // Try seeding (should not add anything because database is not empty)
259+ db. seed_database ( ) . unwrap ( ) ;
260+
261+ // Verify count is only 1 (our custom restaurant)
262+ let final_restaurants = db. list_all ( ) . unwrap ( ) ;
263+ assert_eq ! (
264+ final_restaurants. len( ) ,
265+ 1 ,
266+ "Seeding should not run on non-empty database"
267+ ) ;
268+ assert ! (
269+ final_restaurants. iter( ) . any( |r| r. name == "Custom Restaurant" ) ,
270+ "Custom restaurant should be preserved"
271+ ) ;
272+ }
273+ }
0 commit comments