public async Task Test_FdbMap_List()
        {
            using (var db = await OpenTestPartitionAsync())
            {
                var location = db.Root["Collections"]["Maps"];
                await CleanLocation(db, location);

                var mapFoos = new FdbMap <string, string>(location.ByKey("Foos"), BinaryEncoding.StringEncoder);

                // write a bunch of keys
                await mapFoos.WriteAsync(db, (tr, foos) =>
                {
                    foos.Set(tr, "foo", "foo_value");
                    foos.Set(tr, "bar", "bar_value");
                }, this.Cancellation);

#if FULL_DEBUG
                await DumpSubspace(db, location);
#endif

                // read them back

                await mapFoos.ReadAsync(db, async (tr, foos) =>
                {
                    var value = await foos.GetAsync(tr, "foo");
                    Assert.That(value, Is.EqualTo("foo_value"));

                    value = await foos.GetAsync(tr, "bar");
                    Assert.That(value, Is.EqualTo("bar_value"));

                    Assert.That(async() => await foos.GetAsync(tr, "baz"), Throws.InstanceOf <KeyNotFoundException>());

                    var opt = await foos.TryGetAsync(tr, "baz");
                    Assert.That(opt.HasValue, Is.False);
                }, this.Cancellation);
            }
        }
        public async Task Test_FdbMap_Read_Write_Delete()
        {
            using (var db = await OpenTestPartitionAsync())
            {
                var location = db.Root["Collections"]["Maps"];
                await CleanLocation(db, location);

                var mapFoos = new FdbMap <string, string>(location.ByKey("Foos"), BinaryEncoding.StringEncoder);

                string secret = "world:" + Guid.NewGuid().ToString();

                // read non existing value
                await mapFoos.WriteAsync(db, async (tr, foos) =>
                {
                    Assert.That(async() => await foos.GetAsync(tr, "hello"), Throws.InstanceOf <KeyNotFoundException>());

                    var value = await foos.TryGetAsync(tr, "hello");
                    Assert.That(value.HasValue, Is.False);
                    Assert.That(value.Value, Is.Null);
                }, this.Cancellation);

                // write value
                await mapFoos.WriteAsync(db, (tr, foos) => foos.Set(tr, "hello", secret), this.Cancellation);

#if FULL_DEBUG
                await DumpSubspace(db, location);
#endif

                // read value back
                await mapFoos.ReadAsync(db, async (tr, foos) =>
                {
                    var value = await foos.GetAsync(tr, "hello");
                    Assert.That(value, Is.EqualTo(secret));

                    var opt = await foos.TryGetAsync(tr, "hello");
                    Assert.That(opt.HasValue, Is.True);
                    Assert.That(opt.Value, Is.EqualTo(secret));
                }, this.Cancellation);

                // directly read the value, behind the table's back
                await mapFoos.ReadAsync(db, async (tr, foos) =>
                {
                    var value = await tr.GetAsync(foos.Subspace.AsDynamic().Encode("Foos", "hello"));
                    Assert.That(value, Is.Not.EqualTo(Slice.Nil));
                    Assert.That(value.ToString(), Is.EqualTo(secret));
                }, this.Cancellation);

                // delete the value
                await mapFoos.WriteAsync(db, (tr, foos) => foos.Remove(tr, "hello"), this.Cancellation);

#if FULL_DEBUG
                await DumpSubspace(db, location);
#endif

                // verifiy that it is gone
                await mapFoos.ReadAsync(db, async (tr, foos) =>
                {
                    Assert.That(async() => await foos.GetAsync(tr, "hello"), Throws.InstanceOf <KeyNotFoundException>());

                    var value = await foos.TryGetAsync(tr, "hello");
                    Assert.That(value.HasValue, Is.False);

                    // also check directly
                    var folder = await location.Resolve(tr);
                    var data   = await tr.GetAsync(folder.Encode("Foos", "hello"));
                    Assert.That(data, Is.EqualTo(Slice.Nil));
                }, this.Cancellation);
            }
        }
        public async Task Test_FdbMap_With_Custom_Key_Encoder()
        {
            // Use a table as a backing store for the rules of a Poor Man's firewall, where each keys are the IPEndPoint (tcp only!), and the values are "pass" or "block"

            // Encode IPEndPoint as the (IP, Port,) encoded with the Tuple codec
            // note: there is a much simpler way or creating composite keys, this is just a quick and dirty test!
            var keyEncoder = new KeyEncoder <IPEndPoint>(
                (ipe) => ipe == null ? Slice.Empty : TuPack.EncodeKey(ipe.Address, ipe.Port),
                (packed) =>
            {
                if (packed.IsNullOrEmpty)
                {
                    return(default(IPEndPoint));
                }
                var t = TuPack.Unpack(packed);
                return(new IPEndPoint(t.Get <IPAddress>(0), t.Get <int>(1)));
            }
                );

            var rules = new Dictionary <IPEndPoint, string>()
            {
                { new IPEndPoint(IPAddress.Parse("172.16.12.34"), 6667), "block" },
                { new IPEndPoint(IPAddress.Parse("192.168.34.56"), 80), "pass" },
                { new IPEndPoint(IPAddress.Parse("192.168.34.56"), 443), "pass" }
            };

            using (var db = await OpenTestPartitionAsync())
            {
                var location = db.Root["Collections"]["Maps"];
                await CleanLocation(db, location);

                var mapHosts = new FdbMap <IPEndPoint, string>(location.ByKey("Hosts").AsTyped <IPEndPoint>(keyEncoder), BinaryEncoding.StringEncoder);

                // import all the rules
                await mapHosts.WriteAsync(db, (tr, hosts) =>
                {
                    foreach (var rule in rules)
                    {
                        hosts.Set(tr, rule.Key, rule.Value);
                    }
                }, this.Cancellation);

#if FULL_DEBUG
                await DumpSubspace(db, location);
#endif

                // test the rules

                await mapHosts.ReadAsync(db, async (tr, hosts) =>
                {
                    var value = await hosts.GetAsync(tr, new IPEndPoint(IPAddress.Parse("172.16.12.34"), 6667));
                    Assert.That(value, Is.EqualTo("block"));

                    value = await hosts.GetAsync(tr, new IPEndPoint(IPAddress.Parse("192.168.34.56"), 443));
                    Assert.That(value, Is.EqualTo("pass"));

                    var baz = new IPEndPoint(IPAddress.Parse("172.16.12.34"), 80);
                    Assert.That(async() => await hosts.GetAsync(tr, baz), Throws.InstanceOf <KeyNotFoundException>());

                    var opt = await hosts.TryGetAsync(tr, baz);
                    Assert.That(opt.HasValue, Is.False);
                }, this.Cancellation);
            }
        }