diff --git a/phpunit.xml b/phpunit.xml
index c482167591d2d5470002ce44849a386a652fb975..516e0077b4a6c34b99428ecfb5c93fd5c1b81be5 100755
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -30,6 +30,7 @@
             <file>test/unitTests/app/resource/ResControllerTest.php</file>
             <file>test/unitTests/app/resource/ResourceListControllerTest.php</file>
             <file>test/unitTests/app/resource/ExportControllerTest.php</file>
+            <file>test/unitTests/app/resource/LinkControllerTest.php</file>
             <file>test/unitTests/app/resource/SummarySheetControllerTest.php</file>
             <file>test/unitTests/app/status/StatusControllerTest.php</file>
             <file>test/unitTests/app/shipping/ShippingControllerTest.php</file>
diff --git a/rest/index.php b/rest/index.php
index 586d140b7677601ddd6c2bcc2c4130b06594d5b1..7e159a22f78db63d8880c8248d39b7ba48ec824c 100755
--- a/rest/index.php
+++ b/rest/index.php
@@ -282,9 +282,6 @@ $app->put('/indexingModels/{id}/disable', \IndexingModel\controllers\IndexingMod
 $app->put('/indexingModels/{id}/enable', \IndexingModel\controllers\IndexingModelController::class . ':enable');
 $app->delete('/indexingModels/{id}', \IndexingModel\controllers\IndexingModelController::class . ':delete');
 
-//Links
-$app->get('/links/resId/{resId}', \Link\controllers\LinkController::class . ':getByResId');
-
 //Listinstance
 $app->get('/listinstance/{id}', \Entity\controllers\ListInstanceController::class . ':getById');
 $app->put('/listinstances', \Entity\controllers\ListInstanceController::class . ':update');
diff --git a/src/app/link/controllers/LinkController.php b/src/app/link/controllers/LinkController.php
deleted file mode 100755
index 84951b64b03120d51437bf9b5903471cc2156967..0000000000000000000000000000000000000000
--- a/src/app/link/controllers/LinkController.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * Copyright Maarch since 2008 under licence GPLv3.
- * See LICENCE.txt file at the root folder for more details.
- * This file is part of Maarch software.
- *
- */
-
-/**
- * @brief Link Controller
- * @author dev@maarch.org
- * @ingroup core
- */
-
-namespace Link\controllers;
-
-use Link\models\LinkModel;
-use Respect\Validation\Validator;
-use Slim\Http\Request;
-use Slim\Http\Response;
-
-class LinkController
-{
-    public function getByResId(Request $request, Response $response, $aArgs)
-    {
-        $check = Validator::intVal()->validate($aArgs['resId']);
-        if (!$check) {
-            return $response->withStatus(400)->withJson(['errors' => 'Bad Request']);
-        }
-
-        $aLinks = LinkModel::getByResId(['resId' => $aArgs['resId']]);
-
-        return $response->withJson($aLinks);
-    }
-}
diff --git a/src/app/link/models/LinkModel.php b/src/app/link/models/LinkModel.php
deleted file mode 100755
index f161bb97a1c09360fff13bb03b513b390a1a5447..0000000000000000000000000000000000000000
--- a/src/app/link/models/LinkModel.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-/**
- * Copyright Maarch since 2008 under licence GPLv3.
- * See LICENCE.txt file at the root folder for more details.
- * This file is part of Maarch software.
- *
- */
-
-/**
- * @brief Link Model
- * @author dev@maarch.org
- * @ingroup link
- */
-
-namespace Link\models;
-
-class LinkModel extends LinkModelAbstract
-{
-
-}
diff --git a/src/app/link/models/LinkModelAbstract.php b/src/app/link/models/LinkModelAbstract.php
deleted file mode 100755
index 51c5773843345a6989ebd52ea076f7370b943de1..0000000000000000000000000000000000000000
--- a/src/app/link/models/LinkModelAbstract.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * Copyright Maarch since 2008 under licence GPLv3.
- * See LICENCE.txt file at the root folder for more details.
- * This file is part of Maarch software.
- *
- */
-
-/**
- * @brief Link Model
- * @author dev@maarch.org
- */
-
-namespace Link\models;
-
-use SrcCore\models\DatabaseModel;
-use SrcCore\models\ValidatorModel;
-
-abstract class LinkModelAbstract
-{
-    public static function getByResId(array $aArgs)
-    {
-        ValidatorModel::notEmpty($aArgs, ['resId']);
-        ValidatorModel::intVal($aArgs, ['resId']);
-
-        $aLinks = DatabaseModel::select([
-            'select'    => ['*'],
-            'table'     => ['res_linked'],
-            'where'     => ['res_parent = ? OR res_child = ?'],
-            'data'      => [$aArgs['resId'], $aArgs['resId']]
-        ]);
-
-        return $aLinks;
-    }
-}
diff --git a/src/app/resource/controllers/LinkController.php b/src/app/resource/controllers/LinkController.php
index e736dcd7a7a789fbee6e63b980fe6685dc9f9d96..8b3bb331b28918a7db3f43fe4f1e094dcc69bbbd 100644
--- a/src/app/resource/controllers/LinkController.php
+++ b/src/app/resource/controllers/LinkController.php
@@ -92,7 +92,7 @@ class LinkController
         } elseif (!ResController::hasRightByResId(['resId' => $body['linkedResources'], 'userId' => $GLOBALS['id']])) {
             return $response->withStatus(403)->withJson(['errors' => 'Body linkedResources out of perimeter']);
         } elseif (in_array($args['resId'], $body['linkedResources'])) {
-            return $response->withStatus(403)->withJson(['errors' => 'Body linkedResources contains resource']);
+            return $response->withStatus(400)->withJson(['errors' => 'Body linkedResources contains resource']);
         }
 
         $resource = ResModel::getById(['resId' => $args['resId'], 'select' => ['linked_resources']]);
diff --git a/src/app/signatureBook/controllers/SignatureBookController.php b/src/app/signatureBook/controllers/SignatureBookController.php
index 7a18f6220f032381f934d6dcda837e112a6daa5a..d2c1f5b74fcb481dcb07bb0e51862a1f1ca42217 100755
--- a/src/app/signatureBook/controllers/SignatureBookController.php
+++ b/src/app/signatureBook/controllers/SignatureBookController.php
@@ -106,7 +106,7 @@ class SignatureBookController
         $datas['currentAction']         = $currentAction;
         $datas['resList']               = $resources;
         $datas['nbNotes']               = NoteModel::countByResId(['resId' => $resId, 'userId' => $GLOBALS['id'], 'login' => $GLOBALS['userId']]);
-        $datas['nbLinks']               = count(LinkModel::getByResId(['resId' => $resId]));
+        $datas['nbLinks']               = 0;
         $datas['signatures']            = UserSignatureModel::getByUserSerialId(['userSerialid' => $currentUser['id']]);
         $datas['consigne']              = UserModel::getCurrentConsigneById(['resId' => $resId]);
         $datas['hasWorkflow']           = ((int)$listInstances[0]['count'] > 0);
diff --git a/test/unitTests/app/resource/LinkControllerTest.php b/test/unitTests/app/resource/LinkControllerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..97d3a7ca9f485253e62b7378d83939eedefcc6cf
--- /dev/null
+++ b/test/unitTests/app/resource/LinkControllerTest.php
@@ -0,0 +1,164 @@
+<?php
+
+/**
+* Copyright Maarch since 2008 under licence GPLv3.
+* See LICENCE.txt file at the root folder for more details.
+* This file is part of Maarch software.
+*
+*/
+
+use PHPUnit\Framework\TestCase;
+
+class LinkControllerTest extends TestCase
+{
+    private static $firstResourceId = null;
+    private static $secondResourceId = null;
+
+    public function testLinkResources()
+    {
+        $GLOBALS['userId'] = 'cchaplin';
+        $userInfo = \User\models\UserModel::getByLogin(['login' => $GLOBALS['userId'], 'select' => ['id']]);
+        $GLOBALS['id'] = $userInfo['id'];
+
+        $resController = new \Resource\controllers\ResController();
+
+        //  CREATE
+        $environment    = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'POST']);
+        $request        = \Slim\Http\Request::createFromEnvironment($environment);
+
+
+        $aArgs = [
+            'modelId'       => 1,
+            'status'        => 'NEW',
+            'confidentiality'   => false,
+            'documentDate'  => '2019-01-01 17:18:47',
+            'arrivalDate'   => '2019-01-01 17:18:47',
+            'processLimitDate'  => '2029-01-01',
+            'doctype'       => 102,
+            'destination'   => 15,
+            'initiator'     => 15,
+            'subject'       => 'Lorsque l\'on se cogne la tête contre un pot et que cela sonne creux, ça n\'est pas forcément le pot qui est vide.',
+            'typist'        => 19,
+            'priority'      => 'poiuytre1357nbvc'
+        ];
+
+        $fullRequest = \httpRequestCustom::addContentInBody($aArgs, $request);
+
+        $response     = $resController->create($fullRequest, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+        self::$firstResourceId = $responseBody->resId;
+        $this->assertInternalType('int', self::$firstResourceId);
+
+        $response     = $resController->create($fullRequest, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+        self::$secondResourceId = $responseBody->resId;
+        $this->assertInternalType('int', self::$secondResourceId);
+
+        $GLOBALS['userId'] = 'superadmin';
+        $userInfo = \User\models\UserModel::getByLogin(['login' => $GLOBALS['userId'], 'select' => ['id']]);
+        $GLOBALS['id'] = $userInfo['id'];
+
+
+        $linkController = new \Resource\controllers\LinkController();
+
+        $args = [
+            'linkedResources' => [self::$secondResourceId]
+        ];
+
+        $fullRequest = \httpRequestCustom::addContentInBody($args, $request);
+        $response     = $linkController->linkResources($fullRequest, new \Slim\Http\Response(), ['resId' => self::$firstResourceId]);
+        $this->assertSame(204, $response->getStatusCode());
+
+
+        // ERRORS
+        $args['linkedResources'][] = self::$firstResourceId;
+        $fullRequest = \httpRequestCustom::addContentInBody($args, $request);
+        $response     = $linkController->linkResources($fullRequest, new \Slim\Http\Response(), ['resId' => self::$firstResourceId]);
+        $this->assertSame(400, $response->getStatusCode());
+        $responseBody = json_decode((string)$response->getBody(), true);
+        $this->assertSame('Body linkedResources contains resource', $responseBody['errors']);
+
+        $GLOBALS['userId'] = 'cchaplin';
+        $userInfo = \User\models\UserModel::getByLogin(['login' => $GLOBALS['userId'], 'select' => ['id']]);
+        $GLOBALS['id'] = $userInfo['id'];
+
+        $args['linkedResources'] = [9999999];
+        $fullRequest = \httpRequestCustom::addContentInBody($args, $request);
+        $response     = $linkController->linkResources($fullRequest, new \Slim\Http\Response(), ['resId' => self::$firstResourceId]);
+        $this->assertSame(403, $response->getStatusCode());
+        $responseBody = json_decode((string)$response->getBody(), true);
+        $this->assertSame('Resource out of perimeter', $responseBody['errors']);
+
+        $GLOBALS['userId'] = 'superadmin';
+        $userInfo = \User\models\UserModel::getByLogin(['login' => $GLOBALS['userId'], 'select' => ['id']]);
+        $GLOBALS['id'] = $userInfo['id'];
+
+        $args['linkedResources'] = [];
+        $fullRequest = \httpRequestCustom::addContentInBody($args, $request);
+        $response     = $linkController->linkResources($fullRequest, new \Slim\Http\Response(), ['resId' => self::$firstResourceId]);
+        $this->assertSame(403, $response->getStatusCode());
+        $responseBody = json_decode((string)$response->getBody(), true);
+        $this->assertSame('Body linkedResources is empty or not an array', $responseBody['errors']);
+    }
+
+    public function testGetLinkedResources()
+    {
+        $linkController = new \Resource\controllers\LinkController();
+
+        //  GET
+        $environment    = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'GET']);
+        $request        = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $response     = $linkController->getLinkedResources($request, new \Slim\Http\Response(), ['resId' => self::$firstResourceId]);
+        $this->assertSame(200, $response->getStatusCode());
+        $responseBody = json_decode((string)$response->getBody(), true);
+
+        $this->assertNotEmpty($responseBody['linkedResources']);
+        $this->assertSame(self::$secondResourceId, $responseBody['linkedResources'][0]['resId']);
+        $this->assertSame('Lorsque l\'on se cogne la tête contre un pot et que cela sonne creux, ça n\'est pas forcément le pot qui est vide.', $responseBody['linkedResources'][0]['subject']);
+        $this->assertNotEmpty($responseBody['linkedResources'][0]['status']);
+        $this->assertNotEmpty($responseBody['linkedResources'][0]['destination']);
+        $this->assertNotEmpty($responseBody['linkedResources'][0]['destinationLabel']);
+
+        $response     = $linkController->getLinkedResources($request, new \Slim\Http\Response(), ['resId' => self::$secondResourceId]);
+        $this->assertSame(200, $response->getStatusCode());
+        $responseBody = json_decode((string)$response->getBody(), true);
+
+        $this->assertNotEmpty($responseBody['linkedResources']);
+        $this->assertSame(self::$firstResourceId, $responseBody['linkedResources'][0]['resId']);
+        $this->assertSame('Lorsque l\'on se cogne la tête contre un pot et que cela sonne creux, ça n\'est pas forcément le pot qui est vide.', $responseBody['linkedResources'][0]['subject']);
+        $this->assertNotEmpty($responseBody['linkedResources'][0]['status']);
+        $this->assertNotEmpty($responseBody['linkedResources'][0]['destination']);
+        $this->assertNotEmpty($responseBody['linkedResources'][0]['destinationLabel']);
+    }
+
+    public function testUnlinkResources()
+    {
+        $linkController = new \Resource\controllers\LinkController();
+
+        //  DELETE
+        $environment    = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'DELETE']);
+        $request        = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $response     = $linkController->unlinkResources($request, new \Slim\Http\Response(), ['resId' => self::$firstResourceId, 'id' => self::$secondResourceId]);
+        $this->assertSame(204, $response->getStatusCode());
+
+        $response     = $linkController->getLinkedResources($request, new \Slim\Http\Response(), ['resId' => self::$firstResourceId]);
+        $this->assertSame(200, $response->getStatusCode());
+        $responseBody = json_decode((string)$response->getBody(), true);
+
+        $this->assertEmpty($responseBody['linkedResources']);
+
+        $response     = $linkController->getLinkedResources($request, new \Slim\Http\Response(), ['resId' => self::$secondResourceId]);
+        $this->assertSame(200, $response->getStatusCode());
+        $responseBody = json_decode((string)$response->getBody(), true);
+
+        $this->assertEmpty($responseBody['linkedResources']);
+
+        \SrcCore\models\DatabaseModel::delete([
+            'table' => 'res_letterbox',
+            'where' => ['res_id in (?)'],
+            'data'  => [[self::$firstResourceId, self::$secondResourceId]]
+        ]);
+    }
+}
diff --git a/test/unitTests/define.php b/test/unitTests/define.php
index f0998976832488d8d884ad41df3f486e3e9b0fc7..6bb069ed052cfc3d820aeaa79a921645667b0536 100755
--- a/test/unitTests/define.php
+++ b/test/unitTests/define.php
@@ -12,6 +12,7 @@ require_once 'vendor/autoload.php';
 $userId = 'superadmin';
 $userInfo = \User\models\UserModel::getByLogin(['login' => $userId, 'select' => ['id']]);
 $id = $userInfo['id'];
+$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
 
 date_default_timezone_set(\SrcCore\models\CoreConfigModel::getTimezone());