From 4b9dde655e8df12434cc28078f915a03584c418a Mon Sep 17 00:00:00 2001
From: librelois <elois@ifee.fr>
Date: Mon, 23 Apr 2018 17:45:33 +0200
Subject: [PATCH] [enh] add all wotb methods and all js tests (except io file)

---
 lib/index.js      |  44 +++++---
 native/Cargo.lock |   6 +-
 native/Cargo.toml |   2 +-
 native/src/lib.rs | 129 +++++++++++++++++++-----
 package.json      |   2 +-
 tests/test.js     | 251 +++++++++++++++++++++++++++++++++++++++++++++-
 6 files changed, 384 insertions(+), 50 deletions(-)

diff --git a/lib/index.js b/lib/index.js
index ba91367..d096f0c 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -63,25 +63,19 @@ const WotB = {
     },
 
     getEnabled: function() {
-        var wot_size = addon.get_wot_size(this.instanceID);
-        var enabled_array = new Array();
-        for (let i = 0; i < wot_size; i++) {
-            if (addon.is_enabled(this.instanceID, i)) {
-                enabled_array.push(i);
-            }
-        }
-        return enabled_array;
+        return addon.get_enabled(this.instanceID);
     },
 
     getDisabled: function() {
-        var wot_size = addon.get_wot_size(this.instanceID);
-        var enabled_array = new Array();
-        for (let i = 0; i < wot_size; i++) {
-            if (!addon.is_enabled(this.instanceID, i)) {
-                enabled_array.push(i);
-            }
-        }
-        return enabled_array;
+        return addon.get_disabled(this.instanceID);
+    },
+
+    getSentries: function(sentry_requirement) {
+        return addon.get_sentries(this.instanceID, sentry_requirement);
+    },
+
+    getNonSentries: function(sentry_requirement) {
+        return addon.get_non_sentries(this.instanceID, sentry_requirement);
     },
 
     existsLink: function(source, target) {
@@ -118,6 +112,24 @@ const WotB = {
         return distance_result.outdistanced;
     },
 
+    detailedDistance: function(node, sentry_requirement, step_max, x_percent) {
+        var distance_result = addon.compute_distance(this.instanceID, node, sentry_requirement, step_max, x_percent);
+        return {
+            nbReached: distance_result.reached,
+            nbSuccess: distance_result.success,
+            nbSentries: distance_result.sentries,
+            isOutdistanced: distance_result.outdistanced
+        };
+    },
+
+    detailedDistanceV2: function(node, sentry_requirement, step_max, x_percent) {
+        return addon.compute_distance(this.instanceID, node, sentry_requirement, step_max, x_percent);
+    },
+
+    getPaths: function(from, to, step_max) {
+        return addon.find_paths(this.instanceID, from, to, step_max);
+    },
+
     clear: function() {
         return addon.remove_wot(this.instanceID);
     }
diff --git a/native/Cargo.lock b/native/Cargo.lock
index 095b10c..61a44fc 100644
--- a/native/Cargo.lock
+++ b/native/Cargo.lock
@@ -76,7 +76,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "duniter-wotb"
-version = "0.7.1"
+version = "0.8.0-a0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bincode 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -352,7 +352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 name = "wotb-rs"
 version = "0.1.0"
 dependencies = [
- "duniter-wotb 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "duniter-wotb 0.8.0-a0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "neon 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "neon-build 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -369,7 +369,7 @@ dependencies = [
 "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
 "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
 "checksum cslice 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "697c714f50560202b1f4e2e09cd50a421881c83e9025db75d15f276616f04f40"
-"checksum duniter-wotb 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "834b85bd66552e19e45245c3fe2f96bd350fa11adad679902ae6a127ed9f4dc1"
+"checksum duniter-wotb 0.8.0-a0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "873a948b6755405ec2da9f25b1266fcfaf45f9ef043b08e01eab4a6f91e9e6ad"
 "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
diff --git a/native/Cargo.toml b/native/Cargo.toml
index 715d044..6cf4c9e 100644
--- a/native/Cargo.toml
+++ b/native/Cargo.toml
@@ -15,4 +15,4 @@ neon-build = "0.1.22"
 [dependencies]
 neon = "0.1.22"
 num_cpus = "1.8.0"
-duniter-wotb = "0.7.1"
+duniter-wotb = "0.8.0-a0.5"
diff --git a/native/src/lib.rs b/native/src/lib.rs
index 846077e..52005fb 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -4,15 +4,19 @@ extern crate neon;
 extern crate duniter_wotb;
 extern crate num_cpus;
 
-use duniter_wotb::rusty::RustyWebOfTrust;
-use duniter_wotb::{HasLinkResult, NewLinkResult, NodeId, RemLinkResult, WebOfTrust, WotDistance,
-                   WotDistanceParameters};
+use duniter_wotb::data::rusty::RustyWebOfTrust;
+use duniter_wotb::data::{HasLinkResult, NewLinkResult, NodeId, RemLinkResult, WebOfTrust};
+use duniter_wotb::operations::distance::{DistanceCalculator, RustyDistanceCalculator, WotDistance,
+                                         WotDistanceParameters};
+use duniter_wotb::operations::path::{PathFinder, RustyPathFinder};
 use neon::js::{JsArray, JsBoolean, JsInteger, JsNumber, JsObject, JsString, Object};
 use neon::vm::{Call, JsResult};
 use std::collections::HashMap;
 use std::ops::{Deref, DerefMut};
 
 static mut WOT_INSTANCES: Option<HashMap<usize, Box<RustyWebOfTrust>>> = None;
+static DISTANCE_CALCULATOR: RustyDistanceCalculator = RustyDistanceCalculator {};
+static PATH_FINDER: RustyPathFinder = RustyPathFinder {};
 
 fn get_wots() -> &'static mut HashMap<usize, Box<RustyWebOfTrust>> {
     unsafe {
@@ -82,9 +86,13 @@ fn new_memory_instance(call: Call) -> JsResult<JsInteger> {
 
 fn mem_copy(call: Call) -> JsResult<JsInteger> {
     let scope = call.scope;
-    let instance_id = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+    let instance_id =
+        try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
     let wots = get_wots();
-    let wot_copy = wots.get(&instance_id).expect("This instance don't exist !").deref().clone();
+    let wot_copy = wots.get(&instance_id)
+        .expect("This instance don't exist !")
+        .deref()
+        .clone();
     let mut new_instance_id = 0;
     while wots.contains_key(&new_instance_id) {
         new_instance_id += 1;
@@ -123,18 +131,18 @@ fn is_enabled(call: Call) -> JsResult<JsBoolean> {
     }
 }
 
-fn get_enabled(call: Call) -> JsResult<JsObject> {
+fn get_enabled(call: Call) -> JsResult<JsArray> {
     let scope = call.scope;
     let instance_id = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value();
     let enabled = get_mut_wot(instance_id as usize).get_enabled();
-    let mut js_array = JsObject::new(scope);
+    let mut js_array = JsArray::new(scope, enabled.len() as u32);
     let mut index: u32 = 0;
     for node_id in enabled {
-        let _bool = JsObject::set(
+        let _bool = try!(JsArray::set(
             *js_array.deref_mut(),
             index,
             JsInteger::new(scope, node_id.0 as i32),
-        );
+        ));
         index += 1;
     }
     Ok(js_array)
@@ -147,11 +155,50 @@ fn get_disabled(call: Call) -> JsResult<JsArray> {
     let js_array = JsArray::new(scope, disabled.len() as u32);
     let mut index: u32 = 0;
     for node_id in disabled {
-        let _bool = JsArray::set(
+        let _bool = try!(JsArray::set(
+            *js_array.deref(),
+            index,
+            JsInteger::new(scope, node_id.0 as i32),
+        ));
+        index += 1;
+    }
+    Ok(js_array)
+}
+
+fn get_sentries(call: Call) -> JsResult<JsArray> {
+    let scope = call.scope;
+    let instance_id = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value();
+    let sentry_requirement =
+        try!(try!(call.arguments.require(scope, 1)).check::<JsInteger>()).value();
+    let sentries = get_mut_wot(instance_id as usize).get_sentries(sentry_requirement as usize);
+    let js_array = JsArray::new(scope, sentries.len() as u32);
+    let mut index: u32 = 0;
+    for node_id in sentries {
+        let _bool = try!(JsArray::set(
+            *js_array.deref(),
+            index,
+            JsInteger::new(scope, node_id.0 as i32),
+        ));
+        index += 1;
+    }
+    Ok(js_array)
+}
+
+fn get_non_sentries(call: Call) -> JsResult<JsArray> {
+    let scope = call.scope;
+    let instance_id = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value();
+    let sentry_requirement =
+        try!(try!(call.arguments.require(scope, 1)).check::<JsInteger>()).value();
+    let non_sentries =
+        get_mut_wot(instance_id as usize).get_non_sentries(sentry_requirement as usize);
+    let js_array = JsArray::new(scope, non_sentries.len() as u32);
+    let mut index: u32 = 0;
+    for node_id in non_sentries {
+        let _bool = try!(JsArray::set(
             *js_array.deref(),
             index,
             JsInteger::new(scope, node_id.0 as i32),
-        );
+        ));
         index += 1;
     }
     Ok(js_array)
@@ -252,6 +299,7 @@ fn compute_distance(call: Call) -> JsResult<JsObject> {
         step_max,
         x_percent,
     };
+    let wot = get_wot(instance_id as usize);
     let WotDistance {
         sentries,
         success,
@@ -259,43 +307,71 @@ fn compute_distance(call: Call) -> JsResult<JsObject> {
         reached,
         reached_at_border,
         outdistanced,
-    } = get_wot(instance_id as usize)
-        .compute_distance(distance_params)
+    } = DISTANCE_CALCULATOR
+        .compute_distance(wot, distance_params)
         .unwrap();
     let js_object = JsObject::new(scope);
-    let _bool = JsObject::set(
+    let _bool = try!(JsObject::set(
         *js_object.deref(),
         "sentries",
         JsInteger::new(scope, sentries as i32),
-    );
-    let _bool = JsObject::set(
+    ));
+    let _bool = try!(JsObject::set(
         *js_object.deref(),
         "success",
         JsInteger::new(scope, success as i32),
-    );
-    let _bool = JsObject::set(
+    ));
+    let _bool = try!(JsObject::set(
         *js_object.deref(),
         "success_at_border",
         JsInteger::new(scope, success_at_border as i32),
-    );
-    let _bool = JsObject::set(
+    ));
+    let _bool = try!(JsObject::set(
         *js_object.deref(),
         "reached",
         JsInteger::new(scope, reached as i32),
-    );
-    let _bool = JsObject::set(
+    ));
+    let _bool = try!(JsObject::set(
         *js_object.deref(),
         "reached_at_border",
         JsInteger::new(scope, reached_at_border as i32),
-    );
-    let _bool = JsObject::set(
+    ));
+    let _bool = try!(JsObject::set(
         *js_object.deref(),
         "outdistanced",
         JsBoolean::new(scope, outdistanced),
-    );
+    ));
     Ok(js_object)
 }
 
+fn find_paths(call: Call) -> JsResult<JsArray> {
+    let scope = call.scope;
+    let instance_id =
+        try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+    let from = try!(try!(call.arguments.require(scope, 1)).check::<JsInteger>()).value() as u32;
+    let to = try!(try!(call.arguments.require(scope, 2)).check::<JsInteger>()).value() as u32;
+    let step_max = try!(try!(call.arguments.require(scope, 3)).check::<JsInteger>()).value() as u32;
+    let wot = get_wot(instance_id as usize);
+    let paths = PATH_FINDER.find_paths(wot, NodeId(from as usize), NodeId(to as usize), step_max);
+    let mut i: u32 = 0;
+    let js_paths = JsArray::new(scope, paths.len() as u32);
+    for path in paths {
+        let mut j: u32 = 0;
+        let js_path = JsArray::new(scope, path.len() as u32);
+        for node_id in path {
+            let _bool = try!(JsArray::set(
+                *js_path.deref(),
+                j,
+                JsInteger::new(scope, node_id.0 as i32),
+            ));
+            j += 1;
+        }
+        let _bool = try!(JsArray::set(*js_paths.deref(), i, js_path,));
+        i += 1;
+    }
+    Ok(js_paths)
+}
+
 fn remove_wot(call: Call) -> JsResult<JsBoolean> {
     let scope = call.scope;
     let instance_id =
@@ -319,6 +395,8 @@ register_module!(m, {
     m.export("is_enabled", is_enabled)?;
     m.export("get_enabled", get_enabled)?;
     m.export("get_disabled", get_disabled)?;
+    m.export("get_sentries", get_sentries)?;
+    m.export("get_non_sentries", get_non_sentries)?;
     m.export("exist_link", exist_link)?;
     m.export("set_max_links", set_max_links)?;
     m.export("add_node", add_node)?;
@@ -327,6 +405,7 @@ register_module!(m, {
     m.export("add_link", add_link)?;
     m.export("rem_link", rem_link)?;
     m.export("compute_distance", compute_distance)?;
+    m.export("find_paths", find_paths)?;
     m.export("remove_wot", remove_wot)?;
     Ok(())
 });
diff --git a/package.json b/package.json
index 4785100..292765f 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
   "main": "lib/index.js",
   "repository": {
     "type": "git",
-    "url": "https://git.duniter.org/nodes/typescript/wotb-rust"
+    "url": "https://git.duniter.org/nodes/typescript/wotb-rs"
   },
   "author": "librelois <elois@ifee.fr>",
   "license": "AGPL-3.0",
diff --git a/tests/test.js b/tests/test.js
index 2ebc803..bcdf8fc 100644
--- a/tests/test.js
+++ b/tests/test.js
@@ -30,7 +30,7 @@ function testSuite(title, mode) {
             if (mode === FILE_MODE) {
                 let wot = addon.newFileInstance(FILE);
                 launchTests(wot, () => {
-                    wot.drop();
+                    wot.clear();
                     wot.setMaxCert(3);
                 });
             } else {
@@ -60,12 +60,16 @@ function testSuite(title, mode) {
                 should.equal(wot.addNode(), 0);
                 should.equal(wot.getWoTSize(), 1);
                 should.equal(wot.isEnabled(0), true);
-                should.equal(wot.getEnabled().length, 1);
+                var enabled = wot.getEnabled();
+                should.equal(enabled.length, 1);
+                should.equal(enabled[0], 0);
                 should.equal(wot.getDisabled().length, 0);
                 // Add another
                 should.equal(wot.addNode(), 1);
                 should.equal(wot.getWoTSize(), 2);
-                should.equal(wot.getEnabled().length, 2);
+                var enabled = wot.getEnabled();
+                should.equal(enabled.length, 2);
+                should.equal(enabled[1], 1);
                 should.equal(wot.getDisabled().length, 0);
                 // Add 10 nodes
                 for (let i = 0; i < 10; i++) {
@@ -190,6 +194,13 @@ function testSuite(title, mode) {
                  * 3 --> 2
                  */
                 should.equal(wot.getWoTSize(), 12);
+                should.equal(wot.getSentries(FROM_1_LINK_SENTRIES).length, 1);
+                should.equal(wot.getSentries(FROM_1_LINK_SENTRIES)[0], 2);
+                should.equal(wot.getSentries(FROM_2_LINKS_SENTRIES).length, 0);
+                should.equal(wot.getSentries(FROM_3_LINKS_SENTRIES).length, 0);
+                should.equal(wot.getNonSentries(FROM_1_LINK_SENTRIES).length, 11); // 12 - 1 = 11
+                should.equal(wot.getNonSentries(FROM_2_LINKS_SENTRIES).length, 12); // 12 - 0 = 12
+                should.equal(wot.getNonSentries(FROM_3_LINKS_SENTRIES).length, 12); // 12 - 0 = 12
                 should.equal(wot.isOutdistanced(0, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: 2 --> 0
                 should.equal(wot.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: 2 --> 0
                 should.equal(wot.isOutdistanced(0, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: no sentry with 3 links issued
@@ -199,6 +210,19 @@ function testSuite(title, mode) {
                 wot.addLink(2, 3);
 
                 should.equal(wot.getWoTSize(), 12);
+                should.equal(wot.getSentries(FROM_1_LINK_SENTRIES).length, 3);
+                should.equal(wot.getSentries(FROM_1_LINK_SENTRIES)[0], 1);
+                should.equal(wot.getSentries(FROM_1_LINK_SENTRIES)[1], 2);
+                should.equal(wot.getSentries(FROM_1_LINK_SENTRIES)[2], 3);
+                should.equal(wot.getSentries(FROM_2_LINKS_SENTRIES).length, 1);
+                should.equal(wot.getSentries(FROM_2_LINKS_SENTRIES)[0], 3);
+                should.equal(wot.getSentries(FROM_3_LINKS_SENTRIES).length, 0);
+                should.equal(wot.getNonSentries(FROM_1_LINK_SENTRIES).length, 9); // 12 - 3 = 9
+                should.equal(wot.getNonSentries(FROM_2_LINKS_SENTRIES).length, 11); // 12 - 1 = 11
+                should.equal(wot.getNonSentries(FROM_3_LINKS_SENTRIES).length, 12); // 12 - 0 = 12
+                should.equal(wot.getPaths(3, 0, MAX_DISTANCE_1).length, 0); // KO
+                should.equal(wot.getPaths(3, 0, MAX_DISTANCE_2).length, 1);    // It exists 3 --> 2 --> 0
+                should.equal(wot.getPaths(3, 0, MAX_DISTANCE_2)[0].length, 3); // It exists 3 --> 2 --> 0
                 should.equal(wot.isOutdistanced(0, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OUTDISTANCED__); // KO: No path 3 --> 0
                 should.equal(wot.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OUTDISTANCED__); // KO: No path 3 --> 0
                 should.equal(wot.isOutdistanced(0, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: no sentry with 3 links issued
@@ -220,7 +244,9 @@ function testSuite(title, mode) {
             it('should work with member 3 disabled', function() {
                 // With member 3 disabled (non-member)
                 should.equal(wot.setEnabled(false, 3), false);
-                should.equal(wot.getDisabled().length, 1);
+                let disabled_nodes = wot.getDisabled();
+                should.equal(disabled_nodes.length, 1);
+                should.equal(disabled_nodes[0], 3);
                 should.equal(wot.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: No path 3 --> 0, but is disabled
             });
 
@@ -241,5 +267,222 @@ function testSuite(title, mode) {
 
           after(cleanInstance);
         }));
+
+        describe('Building a larger WoT', newInstance((wot, cleanInstance) => {
+
+            before(() => {
+              cleanInstance();
+              /**
+               * We build WoT:
+               *
+               * 0 --> 1 --> 2 --> 4 --> 5 <==> 6 --> 7
+               *             ^
+               *            ||
+               *            ##==> 3 <-- 8 <-- 9 <========##
+               *                       |                 ||
+               *                       `> 10 <==> 11 <===##
+               */
+              // Add nodes
+              for (let i = 0; i < 12; i++) {
+                should.equal(wot.addNode(), i);
+              }
+              // First line
+              should.equal(wot.addLink(0, 1), 1);
+              should.equal(wot.addLink(1, 2), 1);
+              should.equal(wot.addLink(2, 4), 1);
+              should.equal(wot.addLink(4, 5), 1);
+              should.equal(wot.addLink(5, 6), 1);
+              should.equal(wot.addLink(6, 7), 1);
+              should.equal(wot.addLink(6, 5), 2);
+              // 2n level
+              should.equal(wot.addLink(2, 3), 1);
+              should.equal(wot.addLink(3, 2), 2);
+              should.equal(wot.addLink(8, 3), 2);
+              should.equal(wot.addLink(9, 8), 1);
+              // 3rd level
+              should.equal(wot.addLink(8, 10), 1);
+              should.equal(wot.addLink(10, 11), 1);
+              should.equal(wot.addLink(11, 10), 2);
+              should.equal(wot.addLink(11, 9), 1);
+              should.equal(wot.addLink(9, 11), 2);
+      
+              should.equal(wot.getWoTSize(), 12);
+              return Promise.resolve();
+            });
+      
+            it('should have an initial size of 0', function() {
+              should.equal(wot.getWoTSize(), 12);
+            });
+      
+            describe('testing around 2 with d = 1', () => {
+      
+              /**
+               * Sentries of 1 link (X are not sentries):
+               *
+               * X --> 1 --> 2 --> 4 --> 5 <==> 6 --> X
+               *             ^
+               *            ||
+               *            ##==> 3 <-- 8 <-- 9 <========##
+               *                       |                 ||
+               *                       `> 10 <==> 11 <===##
+               */
+                // => It can be seen 1..6, 8..11 = 10 sentries
+                // => MINUS the sentry #2 (which is tested and is not to be included)
+                // => 9 sentries TESTED against member#2
+      
+              it('should have 10 sentries', function() {
+                should.equal(wot.getSentries(FROM_1_LINK_SENTRIES).length, 10);
+              });
+      
+              it('distance k = 1', function() {
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, _100_PERCENT), __OUTDISTANCED__);
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.5), __OUTDISTANCED__);
+                // 20% of the sentries: OK
+                // => 20% x 9 = 2 sentries to reach
+                // => we have 1 --> 2
+                // => we have 3 --> 2
+                // => OK (1,3)
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.2), __OK__);
+                // Who can pass 20% can pass 10%
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.1), __OK__);
+                // Can pass 23% (1,98 => 2 sentries)
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.22), __OK__);
+                // But cannot pass 23% (2,07 => 3 sentries)
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.23), __OUTDISTANCED__);
+              });
+      
+              it('distance k = 2', function() {
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, _100_PERCENT), __OUTDISTANCED__);
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.5), __OUTDISTANCED__);
+                // 33% of the sentries: OK
+                // => 33% x 9 = 3 sentries to reach
+                // With k = 2 we have the following paths:
+                // 1 --> 2
+                // 8 --> 3 --> 2
+                // => OK (1,8,3)
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.33), __OK__);
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.3), __OK__);
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.2), __OK__);
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.1), __OK__);
+                // But cannot pass 34% (3,06 => 4 sentries)
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.34), __OUTDISTANCED__);
+              });
+      
+              it('distance k = 5', function() {
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, _100_PERCENT), __OUTDISTANCED__);
+                // 66% of the sentries: OK
+                // => 66% x 9 = 6 sentries to reach
+                // With k = 5 we have the following paths:
+                // 1 --> 2
+                // 10 --> 11 --> 9 --> 8 --> 3 --> 2
+                // => OK (1,10,11,9,8,3)
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.66), __OK__);
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.3), __OK__);
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.2), __OK__);
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.1), __OK__);
+                // But cannot pass 67% (6,03 => 7 sentries)
+                should.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.67), __OUTDISTANCED__);
+                assert.deepEqual(wot.detailedDistance(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.67), {
+                  nbReached: 7, // +1 compared to reached sentries, because of member `0`
+                  nbSuccess: 6,
+                  nbSentries: 9,
+                  isOutdistanced: true
+                });
+              });
+            });
+
+            describe('testing around 2 with d = 2', () => {
+                /**
+                 * Sentries of 2 links (X are not sentries):
+                 *
+                 * X --> X --> 2 --> X --> X <==> X --> X
+                 *             ^
+                 *            ||
+                 *            ##==> X <-- X <-- X <========##
+                 *                       |                 ||
+                 *                       `> X  <==> 11 <===##
+                 */
+                  // => It can be seen 2,6,8,9,11 = 5 sentries
+                  // => MINUS the sentry #2 (which is tested and is not to be included)
+                  // => 4 sentries
+        
+                it('should have 2 sentries', function() {
+                  should.equal(wot.getSentries(FROM_2_LINKS_SENTRIES).length, 2);
+                });
+        
+                it('distance k = 1', function() {
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, _100_PERCENT), __OUTDISTANCED__);
+                  // With k = 1 we have no paths
+                  // => ALWAYS KO
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, 0.99), __OUTDISTANCED__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, 0.5), __OUTDISTANCED__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, 0.01), __OUTDISTANCED__);
+                });
+        
+                it('distance k = 2', function() {
+                  // Always distanced with k = 2
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, _100_PERCENT), __OUTDISTANCED__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, 0.25), __OUTDISTANCED__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, 0.24), __OUTDISTANCED__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, 0.251), __OUTDISTANCED__);
+                });
+        
+                it('distance k = 3', function() {
+                  // Always distanced with k = 2
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_3, _100_PERCENT), __OUTDISTANCED__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_3, 0.50), __OUTDISTANCED__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_3, 0.49), __OUTDISTANCED__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_3, 0.51), __OUTDISTANCED__);
+                });
+        
+                it('distance k = 4', function() {
+                  // Only 1 sentry at distance 4: always OK
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_4, _100_PERCENT), __OK__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_4, 0.75), __OK__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_4, 0.01), __OK__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_4, 0.99), __OK__);
+                });
+        
+                it('distance k = 5', function() {
+                  // Only 1 sentry at distance 4: always OK
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_5, _100_PERCENT), __OK__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_5, 0.75), __OK__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_5, 0.01), __OK__);
+                  should.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_5, 0.99), __OK__);
+                });
+            });
+        
+            describe('testing around 2 with d = 3', () => {
+                /**
+                 * Sentries of 3 links (X are not sentries):
+                 *
+                 * X --> X --> 2 --> X --> X <==> X --> X
+                 *             ^
+                 *            ||
+                 *            ##==> X <-- X <-- X <========##
+                 *                       |                 ||
+                 *                       `> X  <==> X <===##
+                 */
+                  // => It can be seen 2 = 1 sentries
+                  // => MINUS the sentry #2 (which is tested and is not to be included)
+                  // => 0 sentries
+                  // => ALWAYS OK, no sentries to constraint
+        
+                it('distance k = 1', function() {
+                  should.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_1, _100_PERCENT), __OK__);
+                  should.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_1, 0.01), __OK__);
+                });
+        
+                it('distance k = 2', function() {
+                  should.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_2, _100_PERCENT), __OK__);
+                  should.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_2, 0.01), __OK__);
+                });
+        
+                it('distance k = 5', function() {
+                  should.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_5, _100_PERCENT), __OK__);
+                  should.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_5, 0.01), __OK__);
+                });
+            });
+        }));
     });
 }
\ No newline at end of file
-- 
GitLab