@@ -21,12 +21,15 @@ import {
2121} from '../validation/name'
2222import {
2323 getAncestorIds ,
24+ validateCreateParentTarget ,
2425 validateNoCircularParent ,
2526 validateNoCircularParentAsync ,
2627} from '../validation/parent'
2728import { validateLocalSidebarMove } from '../validation/move'
2829import { validateItemSlug } from '../validation/slug'
2930import type { Id } from '../../_generated/dataModel'
31+ import { SIDEBAR_ITEM_TYPES } from '../types/baseTypes'
32+ import type { AnySidebarItem } from '../types/types'
3033
3134describe ( 'validateItemName' , ( ) => {
3235 it ( 'accepts a valid name' , ( ) => {
@@ -376,6 +379,25 @@ describe('validateLocalSidebarMove', () => {
376379 expect ( result ) . toEqual ( { valid : true } )
377380 } )
378381
382+ it ( 'rejects missing target parents before other move validation' , ( ) => {
383+ const result = validateLocalSidebarMove (
384+ {
385+ itemId : testId < 'sidebarItems' > ( 'item' ) ,
386+ name : 'Alpha' ,
387+ parentId : testId < 'sidebarItems' > ( 'missing-parent' ) ,
388+ } ,
389+ {
390+ getParent : ( ) => undefined ,
391+ getSiblings : ( ) => [ ] ,
392+ } ,
393+ )
394+
395+ expect ( result ) . toEqual ( {
396+ valid : false ,
397+ error : 'Cannot move item: target parent does not exist' ,
398+ } )
399+ } )
400+
379401 it ( 'checks sibling conflicts for regular moves' , ( ) => {
380402 const result = validateLocalSidebarMove (
381403 {
@@ -393,6 +415,33 @@ describe('validateLocalSidebarMove', () => {
393415 } )
394416} )
395417
418+ describe ( 'validateCreateParentTarget' , ( ) => {
419+ it ( 'rejects path targets whose base parent is not a folder' , ( ) => {
420+ const noteId = testId < 'sidebarItems' > ( 'note-parent' )
421+ const noteParent = {
422+ _id : noteId ,
423+ type : SIDEBAR_ITEM_TYPES . notes ,
424+ parentId : null ,
425+ name : 'Leaf Note' ,
426+ } as unknown as AnySidebarItem
427+
428+ const result = validateCreateParentTarget (
429+ {
430+ kind : 'path' ,
431+ baseParentId : noteId ,
432+ pathSegments : [ ] ,
433+ } ,
434+ new Map ( [ [ noteId , noteParent ] ] ) ,
435+ new Map ( [ [ null , [ noteParent ] ] ] ) ,
436+ )
437+
438+ expect ( result ) . toEqual ( {
439+ valid : false ,
440+ error : 'Parent is not a folder' ,
441+ } )
442+ } )
443+ } )
444+
396445describe ( 'cross-table slug uniqueness' , ( ) => {
397446 const t = createTestContext ( )
398447
0 commit comments