Skip to content

Commit

Permalink
Add tests for MinIO storage plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesChenX committed Dec 17, 2023
1 parent f549d66 commit 2783898
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 3 deletions.
7 changes: 7 additions & 0 deletions turms-plugins/turms-plugin-minio/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>im.turms</groupId>
<artifactId>server-test-common</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,13 @@
*/
public class MinioStorageServiceProvider extends TurmsExtension implements StorageServiceProvider {

public static final String RESOURCE_ID = "id";
public static final String RESOURCE_URL = "url";

private static final Logger LOGGER = LoggerFactory.getLogger(MinioStorageServiceProvider.class);

private static final int INIT_BUCKETS_TIMEOUT_SECONDS = 60;
private static final Map<StorageResourceType, String> RESOURCE_TYPE_TO_BUCKET_NAME;
private static final String RESOURCE_ID = "id";
private static final String RESOURCE_URL = "url";

private static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type";
private static final Mono<Map<String, String>> ERROR_NOT_UPLOADER_OR_SHARED_WITH_USER_TO_DOWNLOAD_MESSAGE_ATTACHMENT =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*
* Copyright (C) 2019 The Turms Project
* https://github.com/turms-im/turms
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.turms.plugin.minio;

import java.io.ByteArrayInputStream;
import java.util.Collections;
import java.util.Map;

import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.context.ApplicationContext;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.test.StepVerifier;

import im.turms.plugin.minio.properties.MinioStorageProperties;
import im.turms.server.common.access.admin.web.MediaType;
import im.turms.server.common.access.admin.web.MediaTypeConst;
import im.turms.server.common.infra.cluster.node.Node;
import im.turms.server.common.infra.property.TurmsProperties;
import im.turms.server.common.infra.property.TurmsPropertiesManager;
import im.turms.server.common.infra.property.env.service.ServiceProperties;
import im.turms.server.common.infra.property.env.service.business.storage.StorageProperties;
import im.turms.server.common.testing.BaseIntegrationTest;
import im.turms.service.domain.group.service.GroupMemberService;
import im.turms.service.domain.user.service.UserRelationshipService;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

/**
* @author James Chen
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class MinioStorageServiceProviderTests extends BaseIntegrationTest {

private static final long USER_ID = 1L;
private static final byte[] FILE_FOR_UPLOADING = new byte[]{0, 1, 2, 3};
private static final String FILE_MEDIA_TYPE = MediaTypeConst.IMAGE_PNG;

private static MinioStorageServiceProvider serviceProvider;

@BeforeAll
static void setup() {
setupTestEnvironment();
}

@Order(0)
@Test
void test_start() {
TurmsPropertiesManager turmsPropertiesManager = mock(TurmsPropertiesManager.class);
when(turmsPropertiesManager.getLocalProperties())
.thenReturn(new TurmsProperties().toBuilder()
.service(new ServiceProperties().toBuilder()
.storage(new StorageProperties().toBuilder()
.build())
.build())
.build());

ApplicationContext applicationContext = mock(ApplicationContext.class);
when(applicationContext.getBean(Node.class)).thenReturn(mock(Node.class));
when(applicationContext.getBean(GroupMemberService.class))
.thenReturn(mock(GroupMemberService.class));
when(applicationContext.getBean(UserRelationshipService.class))
.thenReturn(mock(UserRelationshipService.class));
when(applicationContext.getBean(TurmsPropertiesManager.class))
.thenReturn(turmsPropertiesManager);

MinioStorageServiceProvider provider = spy(new MinioStorageServiceProvider());
when(provider.getContext()).thenReturn(applicationContext);
when(provider.loadProperties(MinioStorageProperties.class))
.thenReturn(new MinioStorageProperties());

StepVerifier.create(provider.start())
.verifyComplete();

serviceProvider = provider;
}

@Order(100)
@Test
void test_queryUserProfilePictureUploadInfo() {
Mono<Map<String, String>> queryUploadInfo =
serviceProvider.queryUserProfilePictureUploadInfo(USER_ID,
null,
MediaType.create(MediaTypeConst.IMAGE_PNG),
Collections.emptyMap());
StepVerifier.create(queryUploadInfo)
.expectNextMatches(uploadInfo -> {
String resourceId = uploadInfo.remove(MinioStorageServiceProvider.RESOURCE_ID);
String resourceUploadUrl =
uploadInfo.remove(MinioStorageServiceProvider.RESOURCE_URL);
assertThat(resourceId).as("The resource ID should not be null")
.isNotNull();
assertThat(resourceUploadUrl).as("The resource upload URL should not be null")
.isNotNull();

StepVerifier.create(HttpClient.create()
.baseUrl(resourceUploadUrl)
.post()
.sendForm((request, form) -> {
for (Map.Entry<String, String> entry : uploadInfo.entrySet()) {
form.attr(entry.getKey(), entry.getValue());
}
form.attr("Content-Type", MediaTypeConst.IMAGE_PNG);
form.attr("key", resourceId);
form.file("file",
resourceId,
new ByteArrayInputStream(FILE_FOR_UPLOADING),
FILE_MEDIA_TYPE);
})
.responseSingle((response, byteBufMono) -> {
assertThat(response.status())
.as("The response status code should be 200")
.isEqualTo(HttpResponseStatus.OK);
return Mono.empty();
}))
.verifyComplete();
return true;
})
.verifyComplete();
}

@Order(101)
@Test
void test_queryUserProfilePictureDownloadInfo() {
Mono<Map<String, String>> queryDownloadInfo = serviceProvider
.queryUserProfilePictureDownloadInfo(USER_ID, USER_ID, Collections.emptyMap());
StepVerifier.create(queryDownloadInfo)
.expectNextMatches(downloadInfo -> {
String resourceDownloadUrl =
downloadInfo.get(MinioStorageServiceProvider.RESOURCE_URL);
StepVerifier.create(HttpClient.create()
.baseUrl(resourceDownloadUrl)
.get()
.responseContent())
.expectNext(Unpooled.wrappedBuffer(FILE_FOR_UPLOADING))
.as("The downloaded file should be the same with the upload file")
.verifyComplete();
return true;
})
.verifyComplete();
}

@Order(102)
@Test
void test_deleteUserProfilePicture() {
Mono<Void> deleteUserProfilePicture =
serviceProvider.deleteUserProfilePicture(USER_ID, Collections.emptyMap());
StepVerifier.create(deleteUserProfilePicture)
.verifyComplete();

Mono<Map<String, String>> queryDownloadInfo = serviceProvider
.queryUserProfilePictureDownloadInfo(USER_ID, USER_ID, Collections.emptyMap());
StepVerifier.create(queryDownloadInfo)
.expectNextMatches(downloadInfo -> {
String resourceDownloadUrl =
downloadInfo.get(MinioStorageServiceProvider.RESOURCE_URL);
StepVerifier.create(HttpClient.create()
.baseUrl(resourceDownloadUrl)
.get()
.response())
.expectNextMatches(response -> {
assertThat(response.status())
.isEqualTo(HttpResponseStatus.NOT_FOUND);
return true;
})
.as("The deleted file should not be found")
.verifyComplete();
return true;
})
.verifyComplete();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import im.turms.server.common.infra.logging.core.logger.LoggerFactory;
import im.turms.server.common.infra.property.TurmsPropertiesManager;
import im.turms.server.common.infra.reactor.TaskScheduler;
import im.turms.server.common.infra.test.VisibleForTesting;

/**
* @author James Chen
Expand Down Expand Up @@ -70,7 +71,11 @@ void setRunning(boolean running) {
this.running = running;
}

protected <T> T loadProperties(Class<T> propertiesClass) {
/**
* The method should be protected, but it is public for testing purposes.
*/
@VisibleForTesting
public <T> T loadProperties(Class<T> propertiesClass) {
return context.getBean(TurmsPropertiesManager.class)
.loadProperties(propertiesClass);
}
Expand Down

0 comments on commit 2783898

Please sign in to comment.