title: JUnit4 - How to test for readonly/write-protected directory if run with docker
body: <p>We have an integration test setup for testing the behavior of missing but required configuration properties. Among one of these properties is a directory where failed uploads should be written to for later retries. The general behavior for this property should be that the application doesn't even start up and fail immediately when certain constraints are violated.</p>
  
  <p>The properties are managed by Spring via certain <code>ConfigurationProperties</code> among these we have a simple <code>S3MessageUploadSettings</code> class </p>
  
  <pre class="lang-java prettyprint-override"><code>@Getter
  @Setter
  @ConfigurationProperties(prefix = "s3")
  @Validated
  public class S3MessageUploadSettings {
      @NotNull
      private String bucketName;
      @NotNull
      private String uploadErrorPath;
      ...
  }
  </code></pre>
  
  <p>In the respective Spring configuration we now perform certain validation checks, like whether the path exists, is writable and a directory, and throw respective <code>RuntimeException</code>s when certain assertions aren't met:</p>
  
  <pre class="lang-java prettyprint-override"><code>@Slf4j
  @Import({ S3Config.class })
  @Configuration
  @EnableConfigurationProperties(S3MessageUploadSettings.class)
  public class S3MessageUploadSpringConfig {
  
      @Resource
      private S3MessageUploadSettings settings;
  
      ...
  
      @PostConstruct
      public void checkConstraints() {
          String sPath = settings.getUploadErrorPath();
          Path path = Paths.get(sPath);
          ...
          log.debug("Probing path '{}' for existence', path);
          if (!Files.exists(path)) {
              throw new RuntimeException("Required error upload directory '" + path + "' does not exist");
          }
  
          log.debug("Probig path '{}' for being a directory", path);
          if (!Files.isDirectory(path)) {
              throw new RuntimeException("Upload directory '" + path + "' is not a directoy");
          }
  
          log.debug("Probing path '{}' for write permissions", path);
          if (!Files.isWritable(path)) {
              throw new RuntimeException("Error upload path '" + path +"' is not writable);
          }
      }
  }
  </code></pre>
  
  <p>Our test setup now looks like this:</p>
  
  <pre class="lang-java prettyprint-override"><code>public class StartupTest {
  
      @ClassRule
      public static TemporaryFolder testFolder = new TemporaryFolder();
  
      private static File BASE_FOLDER;
      private static File ACCESSIBLE;
      private static File WRITE_PROTECTED;
      private static File NON_DIRECTORY;
  
      @BeforeClass
      public static void initFolderSetup() throws IOException {
          BASE_FOLDER = testFolder.getRoot();
          ACCESSIBLE = testFolder.newFolder("accessible");
          WRITE_PROTECTED = testFolder.newFolder("writeProtected");
          if (!WRITE_PROTECTED.setReadOnly()) {
              fail("Could not change directory permissions to readonly")
          }
          if (!WRITE_PROTECTED.setWritable(false)) {
              fail("Could not change directory permissions to writable(false)");
          }
          NON_DIRECTORY = testFolder.newFile("nonDirectory");
      }
  
      @Configuration
      @Import({
          S3MessageUploadSpringConfig.class,
          S3MockConfig.class,
          ...
      })
      static class BaseContextConfig {
          // common bean definitions
          ...
      }
  
      @Configuration
      @Import(BaseContextConfig.class)
      @PropertySource("classpath:ci.properties")
      static class NotExistingPathContextConfig {
  
          @Resource
          private S3MessageUploadSettings settings;
  
          @PostConstruct
          public void updateSettings() {
              settings.setUploadErrorPath(BASE_FOLDER.getPath() + "/foo/bar");
          }
      }
  
      @Configuration
      @Import(BaseContextConfig.class)
      @PropertySource("classpath:ci.properties")
      static class NotWritablePathContextConfig {
  
          @Resource
          private S3MessageUploadSettings settings;
  
          @PostConstruct
          public void updateSettings() {
              settings.setUploadErrorPath(WRITE_PROTECTED.getPath());
          }
      }
  
      ...
  
      @Configuration
      @Import(BaseContextConfig.class)
      @PropertySource("classpath:ci.properties")
      static class StartableContextConfig {
  
          @Resource
          private S3MessageUploadSettings settings;
  
          @PostConstruct
          public void updateSettings() {
              settings.setUploadErrorPath(ACCESSIBLE.getPath());
          }
      }
  
      @Test
      public void shouldFailStartupDueToNonExistingErrorPathDirectory() {
          ApplicationContext context = null;
          try {
              context = new AnnotationConfigApplicationContext(StartupTest.NotExistingPathContextConfig.class);
              fail("Should not have started the context");
          } catch (Exception e) {
              e.printStackTrace();
              assertThat(e, instanceOf(BeanCreationException.class));
              assertThat(e.getMessage(), containsString("Required error upload directory '" + BASE_FOLDER + "/foo/bar' does not exist"));
          } finally {
              closeContext(context);
          }
      }
  
      @Test
      public void shouldFailStartupDueToNonWritablePathDirectory() {
          ApplicationContext context = null;
          try {
              context = new AnnotationConfigApplicationContext(StartupTest.NotWritablePathContextConfig.class);
              fail("Should not have started the context");
          } catch (Exception e) {
              assertThat(e, instanceOf(BeanCreationException.class));
              assertThat(e.getMessage(), containsString("Error upload path '" + WRITE_PROTECTED + "' is not writable"));
          } finally {
              closeContext(context);
          } 
      }
  
      ...
  
      @Test
      public void shouldStartUpSuccessfully() {
          ApplicationContext context = null;
          try {
              context = new AnnotationConfigApplicationContext(StartableContextConfig.class);
          } catch (Exception e) {
              e.printStackTrace();
              fail("Should not have thrown an exception of type " + e.getClass().getSimpleName() + " with message " + e.getMessage());
          } finally {
              closeContext(context);
          }
      }
  
      private void closeContext(ApplicationContext context) {
          if (context != null) {
              // check and close any running S3 mock as this may have negative impact on the startup of a further context
              closeS3Mock(context);
              // stop a running Spring context manually as this might interfere with a starting context of an other test
              ((ConfigurableApplicationContext) context).stop();
          }
      }
  
      private void closeS3Mock(ApplicationContext context) {
          S3Mock s3Mock = null;
          try {
              if (context != null) {
                  s3Mock = context.getBean("s3Mock", S3Mock.class);
              }
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              if (null != s3Mock) {
                  s3Mock.stop();
              }
          }
      }
  }
  </code></pre>
  
  <p>When run locally, everything looks fine and all tests pass. Though our CI runs these tests inside a docker container and for some reason changing file permissions seem to end up in a NOOP returning true on the method invocation though not changing anything in regards of the file permission itself. </p>
  
  <p>Neiter <code>File.setReadOnly()</code>, <code>File.setWritable(false)</code> nor <code>Files.setPosixFilePermissions(Path, Set&lt;PosixFilePermission&gt;)</code> seem to have an effect on the actual file permissions in the docker container.</p>
  
  <p>I've also tried to change the directories to real directories, i.e. <code>/root</code> or <code>/dev/pts</code> that are write protected, though as the CI runs the tests as <code>root</code> these directories are writable by the application and the test fails again.</p>
  
  <p>I also considered using an in-memory file system (such as JimFS) though here I'm not sure how to convince the test to make use of the custom filesystem. AFAIK JimFS does not support the constructor needed for declaring it as default filesystem.</p>
  
  <p>Which other possibilities exist from within Java to change a directories permission to readonly/write-protected when run inside a docker container or test successfully for such a directory?</p>