public void Changesets_can_be_applied_multiple_times()
        {
            var original = new TestingRecord();

            // can store a changeset
            var changeSet = original.StartChangeSet()
                            .Mutate(t => t.AnInteger, 1);

            // applied once
            var record2 = changeSet.ToNewRecord(original);

            Assert.AreEqual(1, record2.AnInteger);
            Assert.AreEqual(new TestingRecord(), original, "The original was changed.");

            var record3 = original.With(copy => copy.Mutate(t => t.AnInteger, 5).AndMutate(t => t.ADouble, 1.0));

            // reapply the stored changeset to a different record
            var record4 = changeSet.ToNewRecord(record3);

            Assert.AreEqual(1, record4.AnInteger);
            Assert.AreEqual(1.0, record4.ADouble, nameof(original.ADouble) + " was not part of th change set and should not have changed.");

            Assert.AreEqual(5, record3.AnInteger, "The original was changed.");
            Assert.AreEqual(1.0, record3.ADouble, "The original was changed.");
        }
        public void A_changeset_with_no_mutators_returns_original_record()
        {
            var original = new TestingRecord();

            var record2 = original.StartChangeSet()
                          .ToNewRecord(original);

            Assert.AreSame(original, record2);
        }
        public void Constraints_are_validated_on_apply()
        {
            var original = new TestingRecord();

            var changeset = original.StartChangeSet()
                            .Mutate(t => t.RecordedAt, DateTimeOffset.Now);

            Assert.Throws <ArgumentException>(() => changeset.ToNewRecord(original));
            Assert.Throws <ArgumentException>(() => original.CopyAndApply(changeset));
        }
        public void Duplicate_property_mutations_replace_earlier_ones()
        {
            var original = new TestingRecord();

            var record2 = original.StartChangeSet()
                          .Mutate(t => t.AnInteger, 1)
                          .AndMutate(t => t.AnInteger, 2)
                          .ToNewRecord(original);

            Assert.AreEqual(2, record2.AnInteger);
        }
        public void Can_mutate_readonly_auto_properties()
        {
            var original = new TestingRecord();

            var record2 = original.StartChangeSet()
                          .Mutate(t => t.ReadonlyAutoBool, true)
                          .ToNewRecord(original);

            Assert.IsTrue(record2.ReadonlyAutoBool);
            Assert.IsFalse(original.ReadonlyAutoBool);
        }
        public void With_copies_and_mutates()
        {
            var original = new TestingRecord();

            Assert.AreEqual(0, original.AnInteger);

            var newRecord = original.With(copy => copy.Mutate(t => t.AnInteger, 1).AndMutate(t => t.ADouble, 1.0));

            Assert.AreEqual(1, newRecord.AnInteger);
            Assert.AreEqual(1.0, newRecord.ADouble);
            Assert.AreEqual(new TestingRecord(), original, "The original was changed.");
        }
        public void Records_can_start_a_new_changeset_and_apply_them()
        {
            var original = new TestingRecord();

            var newRecord = original.StartChangeSet()
                            .Mutate(t => t.AnInteger, 2)
                            .AndMutate(t => t.ADouble, 1.0)
                            .ToNewRecord(original);

            Assert.AreEqual(2, newRecord.AnInteger);
            Assert.AreEqual(1.0, newRecord.ADouble);
            Assert.AreEqual(new TestingRecord(), original, "The original was changed.");
        }
        public void Changesets_can_specify_transforms()
        {
            var original = new TestingRecord();

            var changeSet = original.StartChangeSet()
                            .Mutate(t => t.AnInteger, t => t.AnInteger + 1)
                            .AndMutate(t => t.ADouble, t => t.ADouble + 1);

            var record2 = original.CopyAndApply(changeSet);

            Assert.AreEqual(1, record2.AnInteger);
            Assert.AreEqual(1, record2.ADouble);

            var record3 = record2.CopyAndApply(changeSet);

            Assert.AreEqual(2, record3.AnInteger);
            Assert.AreEqual(2, record3.ADouble);
        }
        public void Can_use_string_but_type_safety_is_not_static()
        {
            var original = new TestingRecord();

            // This method should be a bit more performant becasue it avoids expression walking, but
            // it sacrifices type safety.
            var record2 = original.StartChangeSet()
                          .Mutate(nameof(original.AnInteger), 1)
                          .AndMutate(nameof(original.ADouble), 1.0)
                          .ToNewRecord(original);

            Assert.AreEqual(1, record2.AnInteger);
            Assert.AreEqual(1, record2.ADouble);

            // pitfalls type mismatch
            Assert.Throws <InvalidCastException>(() => original.StartChangeSet().Mutate(nameof(original.RecordedAt), 1));

            // missing property
            Assert.Throws <MissingMemberException>(() => original.StartChangeSet().Mutate("NonExistant", 1));
        }