@@ -1845,6 +1845,138 @@ fn apply_change_with_missing_sequence_insert_key_returns_error_without_panic() {
18451845 ) ;
18461846}
18471847
1848+ #[ test]
1849+ fn apply_change_with_missing_sequence_update_key_returns_error ( ) -> Result < ( ) , AutomergeError > {
1850+ let mut doc = Automerge :: new ( ) ;
1851+ let actor = doc. get_actor ( ) . clone ( ) ;
1852+ let mut tx = doc. transaction ( ) ;
1853+ let list = tx. put_object ( ROOT , "list" , ObjType :: List ) ?;
1854+ tx. insert ( & list, 0 , "seed" ) ?;
1855+ let ( heads, _) = tx. commit ( ) ;
1856+
1857+ let change_actor = ActorId :: from ( b"change" as & [ u8 ] ) ;
1858+ let change = ExpandedChange {
1859+ operations : vec ! [ automerge:: legacy:: Op {
1860+ action: automerge:: legacy:: OpType :: Put ( "orphan" . into( ) ) ,
1861+ obj: automerge:: legacy:: ObjectId :: Id ( automerge:: legacy:: OpId ( 1 , actor. clone( ) ) ) ,
1862+ key: automerge:: legacy:: Key :: Seq ( automerge:: legacy:: ElementId :: Id (
1863+ automerge:: legacy:: OpId ( 999 , actor) ,
1864+ ) ) ,
1865+ pred: automerge:: legacy:: SortedVec :: new( ) ,
1866+ insert: false ,
1867+ } ] ,
1868+ actor_id : change_actor,
1869+ hash : None ,
1870+ seq : 1 ,
1871+ start_op : std:: num:: NonZeroU64 :: MIN ,
1872+ time : 0 ,
1873+ message : None ,
1874+ deps : heads. into_iter ( ) . collect ( ) ,
1875+ extra_bytes : Vec :: new ( ) ,
1876+ } ;
1877+ let change: Change = change. into ( ) ;
1878+ let hash = change. hash ( ) ;
1879+
1880+ let result = doc. apply_changes ( [ change] ) ;
1881+
1882+ assert ! (
1883+ matches!( result, Err ( AutomergeError :: InvalidSeqKey ( _) ) ) ,
1884+ "expected InvalidSeqKey, got {result:?}"
1885+ ) ;
1886+ assert ! ( !doc. get_heads( ) . contains( & hash) ) ;
1887+ Ok ( ( ) )
1888+ }
1889+
1890+ #[ test]
1891+ fn apply_change_with_missing_sequence_delete_key_returns_error ( ) -> Result < ( ) , AutomergeError > {
1892+ let mut doc = Automerge :: new ( ) ;
1893+ let actor = doc. get_actor ( ) . clone ( ) ;
1894+ let mut tx = doc. transaction ( ) ;
1895+ let list = tx. put_object ( ROOT , "list" , ObjType :: List ) ?;
1896+ tx. insert ( & list, 0 , "seed" ) ?;
1897+ let ( heads, _) = tx. commit ( ) ;
1898+
1899+ let change_actor = ActorId :: from ( b"change" as & [ u8 ] ) ;
1900+ let change = ExpandedChange {
1901+ operations : vec ! [ automerge:: legacy:: Op {
1902+ action: automerge:: legacy:: OpType :: Delete ,
1903+ obj: automerge:: legacy:: ObjectId :: Id ( automerge:: legacy:: OpId ( 1 , actor. clone( ) ) ) ,
1904+ key: automerge:: legacy:: Key :: Seq ( automerge:: legacy:: ElementId :: Id (
1905+ automerge:: legacy:: OpId ( 999 , actor) ,
1906+ ) ) ,
1907+ pred: automerge:: legacy:: SortedVec :: new( ) ,
1908+ insert: false ,
1909+ } ] ,
1910+ actor_id : change_actor,
1911+ hash : None ,
1912+ seq : 1 ,
1913+ start_op : std:: num:: NonZeroU64 :: MIN ,
1914+ time : 0 ,
1915+ message : None ,
1916+ deps : heads. into_iter ( ) . collect ( ) ,
1917+ extra_bytes : Vec :: new ( ) ,
1918+ } ;
1919+ let change: Change = change. into ( ) ;
1920+ let hash = change. hash ( ) ;
1921+
1922+ let result = doc. apply_changes ( [ change] ) ;
1923+
1924+ assert ! (
1925+ matches!( result, Err ( AutomergeError :: InvalidSeqKey ( _) ) ) ,
1926+ "expected InvalidSeqKey, got {result:?}"
1927+ ) ;
1928+ assert ! ( !doc. get_heads( ) . contains( & hash) ) ;
1929+ Ok ( ( ) )
1930+ }
1931+
1932+ #[ test]
1933+ fn apply_change_can_reference_same_batch_sequence_insert ( ) -> Result < ( ) , AutomergeError > {
1934+ let mut doc = Automerge :: new ( ) ;
1935+ let actor = doc. get_actor ( ) . clone ( ) ;
1936+ let mut tx = doc. transaction ( ) ;
1937+ let list = tx. put_object ( ROOT , "list" , ObjType :: List ) ?;
1938+ let ( heads, _) = tx. commit ( ) ;
1939+
1940+ let change_actor = ActorId :: from ( b"change" as & [ u8 ] ) ;
1941+ let inserted_id = automerge:: legacy:: OpId ( 1 , change_actor. clone ( ) ) ;
1942+ let change = ExpandedChange {
1943+ operations : vec ! [
1944+ automerge:: legacy:: Op {
1945+ action: automerge:: legacy:: OpType :: Put ( "new" . into( ) ) ,
1946+ obj: automerge:: legacy:: ObjectId :: Id ( automerge:: legacy:: OpId ( 1 , actor. clone( ) ) ) ,
1947+ key: automerge:: legacy:: Key :: Seq ( automerge:: legacy:: ElementId :: Head ) ,
1948+ pred: automerge:: legacy:: SortedVec :: new( ) ,
1949+ insert: true ,
1950+ } ,
1951+ automerge:: legacy:: Op {
1952+ action: automerge:: legacy:: OpType :: Put ( "updated" . into( ) ) ,
1953+ obj: automerge:: legacy:: ObjectId :: Id ( automerge:: legacy:: OpId ( 1 , actor) ) ,
1954+ key: automerge:: legacy:: Key :: Seq ( automerge:: legacy:: ElementId :: Id (
1955+ inserted_id. clone( ) ,
1956+ ) ) ,
1957+ pred: automerge:: legacy:: SortedVec :: from( vec![ inserted_id] ) ,
1958+ insert: false ,
1959+ } ,
1960+ ] ,
1961+ actor_id : change_actor,
1962+ hash : None ,
1963+ seq : 1 ,
1964+ start_op : std:: num:: NonZeroU64 :: MIN ,
1965+ time : 0 ,
1966+ message : None ,
1967+ deps : heads. into_iter ( ) . collect ( ) ,
1968+ extra_bytes : Vec :: new ( ) ,
1969+ } ;
1970+
1971+ doc. apply_changes ( [ change. into ( ) ] ) ?;
1972+
1973+ let Some ( ( value, _) ) = doc. get ( & list, 0 ) ? else {
1974+ panic ! ( "expected list value" ) ;
1975+ } ;
1976+ assert_eq ! ( value. to_str( ) , Some ( "updated" ) ) ;
1977+ Ok ( ( ) )
1978+ }
1979+
18481980#[ test]
18491981fn can_isolate ( ) -> Result < ( ) , AutomergeError > {
18501982 let mut doc1 = AutoCommit :: new ( ) ;
0 commit comments