public void GenerateAndApplyReversePatch()
        {
            var initial = new PhoneBook()
            {
                Entries = new Dictionary <string, TestPerson>()
                {
                    ["0800JOHNDOE"] = new TestPerson()
                    {
                        FirstName = "John",
                        LastName  = "Doe",
                        IsAdult   = true,
                    },
                    ["0800JANEDOE"] = new TestPerson()
                    {
                        FirstName = "Jane",
                        LastName  = "Doe",
                        IsAdult   = true,
                    },
                },
            };

            using DraftScope scope = (DraftScope)DraftExtensions.CreateDraft(initial, out PhoneBook draft);

            var patchGenerator = new DictionaryPatchGenerator();
            var patches        = new JsonPatchDocument();
            var inversePatches = new JsonPatchDocument();

            draft.Entries.Remove("0800JANEDOE");
            draft.Entries.Add("0800BABYDOE", new TestPerson()
            {
                FirstName = "Baby",
                LastName  = "Doe",
            });

            // trick the scope into thinking that is finishing and should not create proxies anymore.
            scope.IsFinishing = true;

            patchGenerator.Generate((IDraft)draft.Entries, "/Entries", patches, inversePatches);

            // inverse order of inverse patches.
            inversePatches.Operations.Reverse();

            var final = scope.FinishDraft <PhoneBook, IPhoneBook>(draft);

            var result = IPhoneBook.Produce(initial, p =>
            {
                patches.ApplyTo(p);
            });

            result = result.Produce(p =>
            {
                inversePatches.ApplyTo(p);
            });

            Assert.Equal(2, result.Entries.Count);
            Assert.Same(initial.Entries["0800JOHNDOE"], result.Entries["0800JOHNDOE"]);
            Assert.Same(initial.Entries["0800JANEDOE"], result.Entries["0800JANEDOE"]);
        }
        public void GenerateDictionaryPatch()
        {
            var initial = new PhoneBook()
            {
                Entries = new Dictionary <string, TestPerson>()
                {
                    ["0800JOHNDOE"] = new TestPerson()
                    {
                        FirstName = "John",
                        LastName  = "Doe",
                        IsAdult   = true,
                    },
                    ["0800JANEDOE"] = new TestPerson()
                    {
                        FirstName = "Jane",
                        LastName  = "Doe",
                        IsAdult   = true,
                    },
                },
            };

            using var scope = DraftExtensions.CreateDraft(initial, out PhoneBook draft);

            var patchGenerator = new DictionaryPatchGenerator();
            var patches        = new JsonPatchDocument();
            var inversePatches = new JsonPatchDocument();

            draft.Entries.Remove("0800JANEDOE");
            draft.Entries.Add("0800BABYDOE", new TestPerson()
            {
                FirstName = "Baby",
                LastName  = "Doe",
            });

            patchGenerator.Generate((IDraft)draft.Entries, "/Entries", patches, inversePatches);

            // inverse order of inverse patches.
            inversePatches.Operations.Reverse();

            JsonAssert.Equal(
                @"
            [
              {
                'path': '/Entries/0800JANEDOE',
                'op': 'remove'
              },
              {
                'value': {
                  'Cars': null,
                  'FirstName': 'Baby',
                  'LastName': 'Doe',
                  'IsAdult': false,
                  'FirstChild': null,
                  'SecondChild': null
                },
                'path': '/Entries/0800BABYDOE',
                'op': 'add'
              }
            ]
            ", JsonConvert.SerializeObject(patches));

            JsonAssert.Equal(
                @"
            [
              {
                'path': '/Entries/0800BABYDOE',
                'op': 'remove'
              },
              {
                'value': {
                  'FirstName': 'Jane',
                  'LastName': 'Doe',
                  'IsAdult': true,
                  'FirstChild': null,
                  'SecondChild': null,
                  'Cars': null
                },
                'path': '/Entries/0800JANEDOE',
                'op': 'add'
              }
            ]
            ", JsonConvert.SerializeObject(inversePatches));
        }