@@ -120,6 +120,17 @@ const MOCK_REGISTRY_BLOCKS: Record<string, any> = {
120120 } ,
121121 ] ,
122122 } ,
123+ table : {
124+ type : 'table' ,
125+ name : 'Table (Legacy)' ,
126+ sunset : { status : 'legacy' , replacedBy : 'table_v2' } ,
127+ subBlocks : [ { id : 'tableId' , type : 'short-input' } ] ,
128+ } ,
129+ table_v2 : {
130+ type : 'table_v2' ,
131+ name : 'Table' ,
132+ subBlocks : [ { id : 'tableId' , type : 'short-input' } ] ,
133+ } ,
123134}
124135const mockGetAllBlocks = getAllBlocks as Mock
125136const mockGetBlock = getBlock as Mock
@@ -875,6 +886,128 @@ describe('minted block ids', () => {
875886 } )
876887} )
877888
889+ describe ( 'sunset block types' , ( ) => {
890+ const legacyId = '44444444-4444-4444-8444-444444444444'
891+ const newId = '55555555-5555-4555-8555-555555555555'
892+ const loopId = '66666666-6666-4666-8666-666666666666'
893+ const insertedId = '77777777-7777-4777-8777-777777777777'
894+
895+ function tableBlock ( id : string , type : string , name : string , parentId ?: string ) {
896+ return {
897+ id,
898+ type,
899+ name,
900+ position : { x : 0 , y : 0 } ,
901+ enabled : true ,
902+ subBlocks : { tableId : { id : 'tableId' , type : 'short-input' , value : 'tbl_old' } } ,
903+ outputs : { } ,
904+ data : parentId ? { parentId } : { } ,
905+ }
906+ }
907+
908+ it ( 'updates a placed legacy child but refuses a new one when a container edit merges children' , ( ) => {
909+ const workflow = {
910+ blocks : {
911+ [ loopId ] : { ...tableBlock ( loopId , 'loop' , 'Each' ) , subBlocks : { } } ,
912+ [ legacyId ] : tableBlock ( legacyId , 'table' , 'Rows' , loopId ) ,
913+ } ,
914+ edges : [ ] ,
915+ loops : { } ,
916+ parallels : { } ,
917+ }
918+
919+ const { state, skippedItems } = applyOperationsToWorkflowState ( workflow , [
920+ {
921+ operation_type : 'edit' ,
922+ block_id : loopId ,
923+ params : {
924+ nestedNodes : {
925+ [ legacyId ] : { type : 'table' , name : 'Rows' , inputs : { tableId : 'tbl_new' } } ,
926+ [ newId ] : { type : 'table' , name : 'Fresh rows' } ,
927+ } ,
928+ } ,
929+ } ,
930+ ] )
931+
932+ expect ( state . blocks [ legacyId ] . subBlocks . tableId . value ) . toBe ( 'tbl_new' )
933+ expect ( state . blocks [ newId ] ) . toBeUndefined ( )
934+ expect ( skippedItems ) . toEqual ( [
935+ expect . objectContaining ( {
936+ type : 'block_not_allowed' ,
937+ operationType : 'add_nested_node' ,
938+ blockId : newId ,
939+ } ) ,
940+ ] )
941+ } )
942+
943+ it ( 'refuses a new legacy block, top-level or nested, and names its successor' , ( ) => {
944+ const { state, skippedItems } = applyOperationsToWorkflowState (
945+ { blocks : { } , edges : [ ] , loops : { } , parallels : { } } ,
946+ [
947+ { operation_type : 'add' , block_id : newId , params : { type : 'table' , name : 'Rows' } } ,
948+ {
949+ operation_type : 'add' ,
950+ block_id : loopId ,
951+ params : {
952+ type : 'loop' ,
953+ name : 'Each' ,
954+ nestedNodes : { [ legacyId ] : { type : 'table' , name : 'Nested rows' } } ,
955+ } ,
956+ } ,
957+ {
958+ operation_type : 'insert_into_subflow' ,
959+ block_id : insertedId ,
960+ params : { type : 'table' , name : 'Inserted rows' , subflowId : loopId } ,
961+ } ,
962+ ]
963+ )
964+
965+ expect ( state . blocks [ newId ] ) . toBeUndefined ( )
966+ expect ( state . blocks [ legacyId ] ) . toBeUndefined ( )
967+ expect ( state . blocks [ insertedId ] ) . toBeUndefined ( )
968+ expect ( state . blocks [ loopId ] ) . toBeDefined ( )
969+ expect ( skippedItems ) . toHaveLength ( 3 )
970+ expect ( skippedItems ) . toEqual (
971+ expect . arrayContaining ( [
972+ expect . objectContaining ( {
973+ type : 'block_not_allowed' ,
974+ operationType : 'add' ,
975+ reason : expect . stringContaining ( 'use "table_v2" instead' ) ,
976+ details : { requestedType : 'table' , replacedBy : 'table_v2' } ,
977+ } ) ,
978+ expect . objectContaining ( { type : 'block_not_allowed' , operationType : 'add_nested_node' } ) ,
979+ expect . objectContaining ( {
980+ type : 'block_not_allowed' ,
981+ operationType : 'insert_into_subflow' ,
982+ } ) ,
983+ ] )
984+ )
985+ } )
986+
987+ it ( 'keeps an existing legacy block editable but refuses switching another block to it' , ( ) => {
988+ const workflow = {
989+ blocks : {
990+ [ legacyId ] : tableBlock ( legacyId , 'table' , 'Rows' ) ,
991+ [ newId ] : tableBlock ( newId , 'table_v2' , 'V2' ) ,
992+ } ,
993+ edges : [ ] ,
994+ loops : { } ,
995+ parallels : { } ,
996+ }
997+
998+ const { state, skippedItems } = applyOperationsToWorkflowState ( workflow , [
999+ { operation_type : 'edit' , block_id : legacyId , params : { inputs : { tableId : 'tbl_new' } } } ,
1000+ { operation_type : 'edit' , block_id : newId , params : { type : 'table' } } ,
1001+ ] )
1002+
1003+ expect ( state . blocks [ legacyId ] . subBlocks . tableId . value ) . toBe ( 'tbl_new' )
1004+ expect ( state . blocks [ newId ] . type ) . toBe ( 'table_v2' )
1005+ expect ( skippedItems ) . toEqual ( [
1006+ expect . objectContaining ( { type : 'block_not_allowed' , operationType : 'edit' , blockId : newId } ) ,
1007+ ] )
1008+ } )
1009+ } )
1010+
8781011describe ( 'permission-group tool access' , ( ) => {
8791012 const denyCanvas = { ...DEFAULT_PERMISSION_GROUP_CONFIG , deniedTools : [ 'slack_canvas' ] }
8801013
0 commit comments