@Test
    public void existentFile() throws Exception {
        final HttpFileAttributes attrs = new HttpFileAttributes(3, 0);
        final ResponseHeaders headers = ResponseHeaders.of(200);
        final HttpFile uncached = mock(HttpFile.class);
        when(uncached.readAttributes(executor)).thenReturn(UnmodifiableFuture.completedFuture(attrs));
        when(uncached.readHeaders(executor)).thenReturn(UnmodifiableFuture.completedFuture(headers));
        when(uncached.read(any(), any())).thenAnswer(invocation -> HttpResponse.of("foo"));
        when(uncached.aggregate(any())).thenAnswer(
                invocation -> UnmodifiableFuture.completedFuture(HttpFile.of(HttpData.ofUtf8("foo"), 0)));

        final HttpFile cached = HttpFile.ofCached(uncached, 3);

        // Ensure readAttributes() is not cached.
        assertThat(cached.readAttributes(executor).join()).isSameAs(attrs);
        verify(uncached, times(1)).readAttributes(executor);
        verifyNoMoreInteractions(uncached);
        clearInvocations(uncached);

        assertThat(cached.readAttributes(executor).join()).isSameAs(attrs);
        verify(uncached, times(1)).readAttributes(executor);
        verifyNoMoreInteractions(uncached);
        clearInvocations(uncached);

        // Ensure readHeaders() is not cached.
        assertThat(cached.readHeaders(executor).join()).isSameAs(headers);
        verify(uncached, times(1)).readHeaders(executor);
        verifyNoMoreInteractions(uncached);
        clearInvocations(uncached);

        assertThat(cached.readHeaders(executor).join()).isSameAs(headers);
        verify(uncached, times(1)).readHeaders(executor);
        verifyNoMoreInteractions(uncached);
        clearInvocations(uncached);

        // First read() should trigger uncached.aggregate().
        HttpResponse res = cached.read(executor, alloc).join();
        assertThat(res).isNotNull();
        assertThat(res.aggregate().join().contentUtf8()).isEqualTo("foo");
        verify(uncached, times(1)).readAttributes(executor);
        verify(uncached, times(1)).aggregate(executor);
        verifyNoMoreInteractions(uncached);
        clearInvocations(uncached);

        // Second read() should not trigger uncached.aggregate().
        res = cached.read(executor, alloc).join();
        assertThat(res).isNotNull();
        assertThat(res.aggregate().join().contentUtf8()).isEqualTo("foo");
        verify(uncached, times(1)).readAttributes(executor);
        verifyNoMoreInteractions(uncached);
        clearInvocations(uncached);

        // Update the uncached file's attributes to invalidate the cache.
        final HttpFileAttributes newAttrs = new HttpFileAttributes(3, 1);
        when(uncached.readAttributes(executor)).thenReturn(UnmodifiableFuture.completedFuture(newAttrs));
        when(uncached.aggregate(any())).thenAnswer(
                invocation -> UnmodifiableFuture.completedFuture(HttpFile.of(HttpData.ofUtf8("bar"), 1)));

        // Make sure read() invalidates the cache and triggers uncached.aggregate().
        res = cached.read(executor, alloc).join();
        assertThat(res).isNotNull();
        assertThat(res.aggregate().join().contentUtf8()).isEqualTo("bar");
        verify(uncached, times(1)).readAttributes(executor);
        verify(uncached, times(1)).aggregate(executor);
        verifyNoMoreInteractions(uncached);
        clearInvocations(uncached);
    }
Makes sure a regular file is handled as expected, including proper cache invalidation.
core/src/test/java/com/linecorp/armeria/server/file/CachingHttpFileTest.java