@@ -322,3 +322,355 @@ impl NoteStore {
322322 } )
323323 }
324324}
325+
326+ #[ cfg( test) ]
327+ mod tests {
328+ use super :: * ;
329+ use gpui:: { Entity , TestAppContext } ;
330+ use rope:: Rope ;
331+
332+ async fn create_test_store ( cx : & mut TestAppContext ) -> Entity < NoteStore > {
333+ cx. executor ( ) . allow_parking ( ) ;
334+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
335+ let db_path = temp_dir. path ( ) . join ( "notes-db" ) ;
336+ let store = cx. update ( |cx| NoteStore :: new ( db_path, cx) ) . await . unwrap ( ) ;
337+ // Leak temp_dir so the database directory outlives the store
338+ std:: mem:: forget ( temp_dir) ;
339+ cx. new ( |_cx| store)
340+ }
341+
342+ // -----------------------------------------------------------------------
343+ // 8.1 — CRUD tests
344+ // -----------------------------------------------------------------------
345+
346+ #[ gpui:: test]
347+ async fn create_and_load ( cx : & mut TestAppContext ) {
348+ let store = create_test_store ( cx) . await ;
349+
350+ let id = NoteId :: new ( ) ;
351+ store
352+ . update ( cx, |s, cx| {
353+ s. save (
354+ id,
355+ Some ( "Test Note" . into ( ) ) ,
356+ false ,
357+ vec ! [ ] ,
358+ Rope :: from ( "Hello, world!" ) ,
359+ cx,
360+ )
361+ } )
362+ . await
363+ . unwrap ( ) ;
364+
365+ let body = store. update ( cx, |s, _cx| s. load_body_sync ( id) ) . unwrap ( ) ;
366+ assert_eq ! ( body, "Hello, world!" ) ;
367+
368+ let meta = store. update ( cx, |s, _cx| s. metadata ( id) ) . unwrap ( ) ;
369+ assert_eq ! ( meta. title, Some ( SharedString :: from( "Test Note" ) ) ) ;
370+ assert ! ( !meta. default ) ;
371+ assert ! ( meta. assigned_automations. is_empty( ) ) ;
372+ }
373+
374+ #[ gpui:: test]
375+ async fn update_title_and_body ( cx : & mut TestAppContext ) {
376+ let store = create_test_store ( cx) . await ;
377+
378+ let id = NoteId :: new ( ) ;
379+ store
380+ . update ( cx, |s, cx| {
381+ s. save (
382+ id,
383+ Some ( "Original" . into ( ) ) ,
384+ false ,
385+ vec ! [ ] ,
386+ Rope :: from ( "original body" ) ,
387+ cx,
388+ )
389+ } )
390+ . await
391+ . unwrap ( ) ;
392+
393+ store
394+ . update ( cx, |s, cx| {
395+ s. save (
396+ id,
397+ Some ( "Updated" . into ( ) ) ,
398+ false ,
399+ vec ! [ ] ,
400+ Rope :: from ( "updated body" ) ,
401+ cx,
402+ )
403+ } )
404+ . await
405+ . unwrap ( ) ;
406+
407+ let meta = store. update ( cx, |s, _cx| s. metadata ( id) ) . unwrap ( ) ;
408+ assert_eq ! ( meta. title, Some ( SharedString :: from( "Updated" ) ) ) ;
409+
410+ let body = store. update ( cx, |s, _cx| s. load_body_sync ( id) ) . unwrap ( ) ;
411+ assert_eq ! ( body, "updated body" ) ;
412+ }
413+
414+ #[ gpui:: test]
415+ async fn delete_removes ( cx : & mut TestAppContext ) {
416+ let store = create_test_store ( cx) . await ;
417+
418+ let id = NoteId :: new ( ) ;
419+ store
420+ . update ( cx, |s, cx| {
421+ s. save (
422+ id,
423+ Some ( "Doomed" . into ( ) ) ,
424+ false ,
425+ vec ! [ ] ,
426+ Rope :: from ( "bye" ) ,
427+ cx,
428+ )
429+ } )
430+ . await
431+ . unwrap ( ) ;
432+
433+ store
434+ . update ( cx, |s, cx| s. delete ( id, cx) )
435+ . await
436+ . unwrap ( ) ;
437+
438+ let meta = store. update ( cx, |s, _cx| s. metadata ( id) ) ;
439+ assert ! ( meta. is_none( ) ) ;
440+
441+ let body = store. update ( cx, |s, _cx| s. load_body_sync ( id) ) ;
442+ assert ! ( body. is_err( ) ) ;
443+ }
444+
445+ #[ gpui:: test]
446+ async fn all_metadata_returns_all ( cx : & mut TestAppContext ) {
447+ let store = create_test_store ( cx) . await ;
448+
449+ for i in 0 ..3 {
450+ store
451+ . update ( cx, |s, cx| {
452+ s. save (
453+ NoteId :: new ( ) ,
454+ Some ( SharedString :: from ( format ! ( "Note {i}" ) ) ) ,
455+ false ,
456+ vec ! [ ] ,
457+ Rope :: from ( "body" ) ,
458+ cx,
459+ )
460+ } )
461+ . await
462+ . unwrap ( ) ;
463+ }
464+
465+ let all = store. update ( cx, |s, _cx| s. all_metadata ( ) ) ;
466+ assert_eq ! ( all. len( ) , 3 ) ;
467+ }
468+
469+ #[ gpui:: test]
470+ async fn first_returns_alphabetically ( cx : & mut TestAppContext ) {
471+ let store = create_test_store ( cx) . await ;
472+
473+ store
474+ . update ( cx, |s, cx| {
475+ s. save (
476+ NoteId :: new ( ) ,
477+ Some ( "Zebra" . into ( ) ) ,
478+ false ,
479+ vec ! [ ] ,
480+ Rope :: from ( "z" ) ,
481+ cx,
482+ )
483+ } )
484+ . await
485+ . unwrap ( ) ;
486+
487+ store
488+ . update ( cx, |s, cx| {
489+ s. save (
490+ NoteId :: new ( ) ,
491+ Some ( "Alpha" . into ( ) ) ,
492+ false ,
493+ vec ! [ ] ,
494+ Rope :: from ( "a" ) ,
495+ cx,
496+ )
497+ } )
498+ . await
499+ . unwrap ( ) ;
500+
501+ let first = store. update ( cx, |s, _cx| s. first ( ) ) . unwrap ( ) ;
502+ assert_eq ! ( first. title, Some ( SharedString :: from( "Alpha" ) ) ) ;
503+ }
504+
505+ // -----------------------------------------------------------------------
506+ // 8.2 — Query tests
507+ // -----------------------------------------------------------------------
508+
509+ #[ gpui:: test]
510+ async fn default_metadata_filters ( cx : & mut TestAppContext ) {
511+ let store = create_test_store ( cx) . await ;
512+
513+ store
514+ . update ( cx, |s, cx| {
515+ s. save (
516+ NoteId :: new ( ) ,
517+ Some ( "Default note" . into ( ) ) ,
518+ true ,
519+ vec ! [ ] ,
520+ Rope :: from ( "d" ) ,
521+ cx,
522+ )
523+ } )
524+ . await
525+ . unwrap ( ) ;
526+
527+ store
528+ . update ( cx, |s, cx| {
529+ s. save (
530+ NoteId :: new ( ) ,
531+ Some ( "Regular note" . into ( ) ) ,
532+ false ,
533+ vec ! [ ] ,
534+ Rope :: from ( "r" ) ,
535+ cx,
536+ )
537+ } )
538+ . await
539+ . unwrap ( ) ;
540+
541+ let defaults = store. update ( cx, |s, _cx| s. default_metadata ( ) ) ;
542+ assert_eq ! ( defaults. len( ) , 1 ) ;
543+ assert_eq ! ( defaults[ 0 ] . title, Some ( SharedString :: from( "Default note" ) ) ) ;
544+ }
545+
546+ #[ gpui:: test]
547+ async fn notes_for_automation_filters ( cx : & mut TestAppContext ) {
548+ let store = create_test_store ( cx) . await ;
549+
550+ // A default note (applies to all)
551+ store
552+ . update ( cx, |s, cx| {
553+ s. save (
554+ NoteId :: new ( ) ,
555+ Some ( "Default" . into ( ) ) ,
556+ true ,
557+ vec ! [ ] ,
558+ Rope :: from ( "d" ) ,
559+ cx,
560+ )
561+ } )
562+ . await
563+ . unwrap ( ) ;
564+
565+ // Assigned to "build-report"
566+ store
567+ . update ( cx, |s, cx| {
568+ s. save (
569+ NoteId :: new ( ) ,
570+ Some ( "Build rules" . into ( ) ) ,
571+ false ,
572+ vec ! [ "build-report" . into( ) ] ,
573+ Rope :: from ( "b" ) ,
574+ cx,
575+ )
576+ } )
577+ . await
578+ . unwrap ( ) ;
579+
580+ // Assigned to "mix-check"
581+ store
582+ . update ( cx, |s, cx| {
583+ s. save (
584+ NoteId :: new ( ) ,
585+ Some ( "Mix rules" . into ( ) ) ,
586+ false ,
587+ vec ! [ "mix-check" . into( ) ] ,
588+ Rope :: from ( "m" ) ,
589+ cx,
590+ )
591+ } )
592+ . await
593+ . unwrap ( ) ;
594+
595+ let build_notes =
596+ store. update ( cx, |s, _cx| s. notes_for_automation ( "build-report" ) ) ;
597+ assert_eq ! ( build_notes. len( ) , 2 ) ; // default + assigned
598+
599+ let unknown_notes =
600+ store. update ( cx, |s, _cx| s. notes_for_automation ( "unknown" ) ) ;
601+ assert_eq ! ( unknown_notes. len( ) , 1 ) ; // default only
602+ }
603+
604+ // -----------------------------------------------------------------------
605+ // 8.3 — Assignment round-trip tests
606+ // -----------------------------------------------------------------------
607+
608+ #[ gpui:: test]
609+ async fn save_metadata_updates_assignments ( cx : & mut TestAppContext ) {
610+ let store = create_test_store ( cx) . await ;
611+
612+ let id = NoteId :: new ( ) ;
613+ store
614+ . update ( cx, |s, cx| {
615+ s. save (
616+ id,
617+ Some ( "Note" . into ( ) ) ,
618+ false ,
619+ vec ! [ "a" . into( ) , "b" . into( ) ] ,
620+ Rope :: from ( "body" ) ,
621+ cx,
622+ )
623+ } )
624+ . await
625+ . unwrap ( ) ;
626+
627+ store
628+ . update ( cx, |s, cx| {
629+ s. save_metadata (
630+ id,
631+ Some ( "Note" . into ( ) ) ,
632+ false ,
633+ vec ! [ "b" . into( ) , "c" . into( ) ] ,
634+ cx,
635+ )
636+ } )
637+ . await
638+ . unwrap ( ) ;
639+
640+ let meta = store. update ( cx, |s, _cx| s. metadata ( id) ) . unwrap ( ) ;
641+ assert_eq ! ( meta. assigned_automations, vec![ "b" , "c" ] ) ;
642+ }
643+
644+ #[ gpui:: test]
645+ async fn clear_assignments ( cx : & mut TestAppContext ) {
646+ let store = create_test_store ( cx) . await ;
647+
648+ let id = NoteId :: new ( ) ;
649+ store
650+ . update ( cx, |s, cx| {
651+ s. save (
652+ id,
653+ Some ( "Note" . into ( ) ) ,
654+ false ,
655+ vec ! [ "a" . into( ) ] ,
656+ Rope :: from ( "body" ) ,
657+ cx,
658+ )
659+ } )
660+ . await
661+ . unwrap ( ) ;
662+
663+ store
664+ . update ( cx, |s, cx| {
665+ s. save_metadata ( id, Some ( "Note" . into ( ) ) , false , vec ! [ ] , cx)
666+ } )
667+ . await
668+ . unwrap ( ) ;
669+
670+ let notes = store. update ( cx, |s, _cx| s. notes_for_automation ( "a" ) ) ;
671+ assert ! (
672+ notes. iter( ) . all( |n| n. id != id) ,
673+ "cleared note should not appear for automation 'a'"
674+ ) ;
675+ }
676+ }
0 commit comments