From 8668e280cedf00c4e0b302f5d616e1213f2dce83 Mon Sep 17 00:00:00 2001
From: Tigran Mkrtchyan <tigran.mkrtchyan@desy.de>
Date: Mon, 24 Oct 2022 11:17:05 +0200
Subject: [PATCH] test: add test to check NFSClient lease expiration

Acked-by: Lea Morschel
Target: master
---
 .../org/dcache/nfs/v4/NFSv4StateHandler.java  |  9 ++++-
 .../org/dcache/nfs/v4/NFS4ClientTest.java     | 36 +++++++++++++++++--
 2 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/core/src/main/java/org/dcache/nfs/v4/NFSv4StateHandler.java b/core/src/main/java/org/dcache/nfs/v4/NFSv4StateHandler.java
index afccb8295..4e8c00cb5 100644
--- a/core/src/main/java/org/dcache/nfs/v4/NFSv4StateHandler.java
+++ b/core/src/main/java/org/dcache/nfs/v4/NFSv4StateHandler.java
@@ -19,6 +19,7 @@
  */
 package org.dcache.nfs.v4;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import java.time.Clock;
 import java.time.Duration;
@@ -106,7 +107,7 @@ public class NFSv4StateHandler {
     /**
      * Clock to use for all time related operations.
      */
-    private final Clock _clock = Clock.systemDefaultZone();
+    private final Clock _clock;
 
     public NFSv4StateHandler() {
         this(Duration.ofSeconds(NFSv4Defaults.NFS4_LEASE_TIME), 0, new EphemeralClientRecoveryStore());
@@ -125,8 +126,14 @@ public NFSv4StateHandler(Duration leaseTime, int instanceId, ClientRecoveryStore
     }
 
     public NFSv4StateHandler(Duration leaseTime, int instanceId, ClientRecoveryStore clientStore, ClientCache clientsByServerId) {
+        this(leaseTime, instanceId, clientStore, new DefaultClientCache(leaseTime, new DeadClientCollector(clientStore)), Clock.systemDefaultZone());
+    }
+
+    @VisibleForTesting
+    NFSv4StateHandler(Duration leaseTime, int instanceId, ClientRecoveryStore clientStore, ClientCache clientsByServerId, Clock clock) {
         _leaseTime = leaseTime;
         _clientsByServerId = clientsByServerId;
+        _clock = clock;
 
         _running = true;
         _instanceId = instanceId;
diff --git a/core/src/test/java/org/dcache/nfs/v4/NFS4ClientTest.java b/core/src/test/java/org/dcache/nfs/v4/NFS4ClientTest.java
index 04ab73579..f9782803f 100644
--- a/core/src/test/java/org/dcache/nfs/v4/NFS4ClientTest.java
+++ b/core/src/test/java/org/dcache/nfs/v4/NFS4ClientTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 - 2020 Deutsches Elektronen-Synchroton,
+ * Copyright (c) 2009 - 2022 Deutsches Elektronen-Synchroton,
  * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
  *
  * This library is free software; you can redistribute it and/or modify
@@ -21,11 +21,15 @@
 
 import java.net.UnknownHostException;
 import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.dcache.nfs.ChimeraNFSException;
 import org.dcache.nfs.nfsstat;
 import org.dcache.nfs.status.SeqMisorderedException;
+import org.dcache.nfs.util.ManualClock;
+import org.dcache.nfs.util.NopCacheEventListener;
 import org.dcache.nfs.v4.xdr.nfs_argop4;
 import org.dcache.nfs.v4.xdr.nfs_opnum4;
 import org.dcache.nfs.v4.xdr.nfs_resop4;
@@ -49,12 +53,20 @@ public class NFS4ClientTest {
     private NFSv4StateHandler stateHandler;
     private NFS4Client nfsClient;
     private StateOwner owner;
+    private ManualClock clock;
 
     @Before
     public void setUp() throws UnknownHostException, ChimeraNFSException {
-        stateHandler = new NFSv4StateHandler();
+
+        clock = new ManualClock();
+        var leaseTime = Duration.ofSeconds(NFSv4Defaults.NFS4_LEASE_TIME);
+        var clientStore = new EphemeralClientRecoveryStore();
+        stateHandler = new NFSv4StateHandler(leaseTime, 0, clientStore,
+              new DefaultClientCache(leaseTime, new NopCacheEventListener<>()), clock);
+
         nfsClient = createClient(stateHandler);
-        owner = nfsClient.getOrCreateOwner("client test".getBytes(StandardCharsets.UTF_8), new seqid4(0));
+        owner = nfsClient.getOrCreateOwner("client test".getBytes(StandardCharsets.UTF_8),
+              new seqid4(0));
     }
 
     @Test
@@ -171,5 +183,23 @@ public void testClientDisposeCleansState() throws ChimeraNFSException {
         assertTrue("client state is not disposed", isDisposed.get());
         assertFalse("client claims to have a state after dispose", nfsClient.hasState());
     }
+
+    @Test
+    public void testClientValidityBeforeLeaseExpired() throws ChimeraNFSException {
+
+        assertTrue(nfsClient.isLeaseValid());
+
+        clock.advance(stateHandler.getLeaseTime().minus(1, ChronoUnit.SECONDS));
+        assertTrue("Client should be valid before lease have expired.", nfsClient.isLeaseValid());
+    }
+
+    @Test
+    public void testClientValidityAfterLeaseExpired() throws ChimeraNFSException {
+
+        assertTrue(nfsClient.isLeaseValid());
+
+        clock.advance(stateHandler.getLeaseTime().plus(1, ChronoUnit.SECONDS));
+        assertFalse("Client can be valid with expired lease", nfsClient.isLeaseValid());
+    }
 }