@Override
    public Mono<Void> backup(Backup backup) {
        return Mono.usingWhen(
            createTempDir("halo-full-backup-", scheduler),
            tempDir -> backupExtensions(tempDir)
                .then(Mono.defer(() -> backupWorkDir(tempDir)))
                .then(Mono.defer(() -> packageBackup(tempDir, backup))),
            tempDir -> deleteRecursivelyAndSilently(tempDir, scheduler)
        );
    }
@Test
    void backupTest() throws IOException {
        Files.writeString(tempDir.resolve("fake-file"), "halo", StandardOpenOption.CREATE_NEW);
        var extensionStores = List.of(
            createExtensionStore("fake-extension-store", "fake-data")
        );
        when(repository.findAll()).thenReturn(Flux.fromIterable(extensionStores));
        when(haloProperties.getWorkDir()).thenReturn(tempDir);
        when(backupRoot.get()).thenReturn(tempDir.resolve("backups"));
        var startTimestamp = Instant.now();
        var backup = createRunningBackup("fake-backup", startTimestamp);
        StepVerifier.create(migrationService.backup(backup))
            .verifyComplete();

        verify(repository).findAll();
        // 1. backup workdir
        // 2. package backup
        verify(haloProperties).getWorkDir();
        verify(backupRoot).get();

        var status = backup.getStatus();
        var datetimePart = migrationService.getDateTimeFormatter().format(startTimestamp);
        assertEquals(datetimePart + "-fake-backup.zip", status.getFilename());
        var backupFile = migrationService.getBackupsRoot()
            .resolve(status.getFilename());
        assertTrue(Files.exists(backupFile));
        assertEquals(Files.size(backupFile), status.getSize());

        var target = tempDir.resolve("target");
        try (var zis = new ZipInputStream(
            Files.newInputStream(backupFile, StandardOpenOption.READ))) {
            FileUtils.unzip(zis, tempDir.resolve("target"));
        }

        var extensionsFile = target.resolve("extensions.data");
        var workdir = target.resolve("workdir");
        assertTrue(Files.exists(extensionsFile));
        assertTrue(Files.exists(workdir));

        var objectMapper = migrationService.getObjectMapper();
        var gotExtensionStores = objectMapper.readValue(extensionsFile.toFile(),
            new TypeReference<List<ExtensionStore>>() {
            });
        assertEquals(gotExtensionStores, extensionStores);
        assertEquals("halo", Files.readString(workdir.resolve("fake-file")));
    }