@@ -847,3 +847,116 @@ describe('defineStack - Map Format Support', () => {
847847 expect ( result . views ! [ 0 ] . list ?. type ) . toBe ( 'grid' ) ;
848848 } ) ;
849849} ) ;
850+
851+ // ============================================================================
852+ // Negative / Inverse Validation Tests — Cross-Reference
853+ // ============================================================================
854+
855+ describe ( 'defineStack - Seed Data Cross-Reference Validation' , ( ) => {
856+ const baseManifest = {
857+ id : 'com.example.test' ,
858+ name : 'test-project' ,
859+ version : '1.0.0' ,
860+ type : 'app' as const ,
861+ } ;
862+
863+ it ( 'should detect seed data referencing undefined object' , ( ) => {
864+ const config = {
865+ manifest : baseManifest ,
866+ objects : [
867+ { name : 'account' , fields : { name : { type : 'text' } } } ,
868+ ] ,
869+ data : [
870+ { object : 'ghost_object' , records : [ { name : 'Test' } ] } ,
871+ ] ,
872+ } ;
873+ expect ( ( ) => defineStack ( config ) ) . toThrow ( 'ghost_object' ) ;
874+ expect ( ( ) => defineStack ( config ) ) . toThrow ( 'cross-reference validation failed' ) ;
875+ } ) ;
876+
877+ it ( 'should pass when seed data references defined object' , ( ) => {
878+ const config = {
879+ manifest : baseManifest ,
880+ objects : [
881+ { name : 'account' , fields : { name : { type : 'text' } } } ,
882+ ] ,
883+ data : [
884+ { object : 'account' , records : [ { name : 'Acme Corp' } ] } ,
885+ ] ,
886+ } ;
887+ expect ( ( ) => defineStack ( config ) ) . not . toThrow ( ) ;
888+ } ) ;
889+ } ) ;
890+
891+ describe ( 'defineStack - Navigation Cross-Reference Validation' , ( ) => {
892+ const baseManifest = {
893+ id : 'com.example.test' ,
894+ name : 'test-project' ,
895+ version : '1.0.0' ,
896+ type : 'app' as const ,
897+ } ;
898+
899+ it ( 'should detect navigation referencing undefined object' , ( ) => {
900+ const config = {
901+ manifest : baseManifest ,
902+ objects : [
903+ { name : 'task' , fields : { title : { type : 'text' } } } ,
904+ ] ,
905+ apps : [
906+ {
907+ name : 'my_app' ,
908+ label : 'My App' ,
909+ navigation : [
910+ { id : 'nav_missing' , type : 'object' as const , label : 'Missing' , objectName : 'nonexistent_object' } ,
911+ ] ,
912+ } ,
913+ ] ,
914+ } ;
915+ expect ( ( ) => defineStack ( config ) ) . toThrow ( 'nonexistent_object' ) ;
916+ } ) ;
917+
918+ it ( 'should detect navigation referencing undefined dashboard' , ( ) => {
919+ const config = {
920+ manifest : baseManifest ,
921+ objects : [
922+ { name : 'task' , fields : { title : { type : 'text' } } } ,
923+ ] ,
924+ dashboards : [
925+ { name : 'sales_dashboard' , label : 'Sales' , widgets : [ ] } ,
926+ ] ,
927+ apps : [
928+ {
929+ name : 'my_app' ,
930+ label : 'My App' ,
931+ navigation : [
932+ { id : 'nav_ghost' , type : 'dashboard' as const , label : 'Missing' , dashboardName : 'ghost_dashboard' } ,
933+ ] ,
934+ } ,
935+ ] ,
936+ } ;
937+ expect ( ( ) => defineStack ( config ) ) . toThrow ( 'ghost_dashboard' ) ;
938+ } ) ;
939+
940+ it ( 'should pass when all navigation references are valid' , ( ) => {
941+ const config = {
942+ manifest : baseManifest ,
943+ objects : [
944+ { name : 'task' , fields : { title : { type : 'text' } } } ,
945+ ] ,
946+ dashboards : [
947+ { name : 'task_overview' , label : 'Overview' , widgets : [ ] } ,
948+ ] ,
949+ apps : [
950+ {
951+ name : 'my_app' ,
952+ label : 'My App' ,
953+ navigation : [
954+ { id : 'nav_tasks' , type : 'object' as const , label : 'Tasks' , objectName : 'task' } ,
955+ { id : 'nav_overview' , type : 'dashboard' as const , label : 'Overview' , dashboardName : 'task_overview' } ,
956+ ] ,
957+ } ,
958+ ] ,
959+ } ;
960+ expect ( ( ) => defineStack ( config ) ) . not . toThrow ( ) ;
961+ } ) ;
962+ } ) ;
0 commit comments