Compare commits

...

2 Commits

Author SHA1 Message Date
ayan d96c9a54e2 设备模块单元测试 2022-11-02 17:46:12 +08:00
zhangji 8691833c97 单元测试 2022-11-02 10:36:22 +08:00
36 changed files with 3146 additions and 66 deletions

View File

@ -0,0 +1,45 @@
#!/bin/bash
#set -x
#******************************************************************************
# @file : entrypoint.sh
# @author : wangyubin
# @date : 2018-08- 1 10:18:43
#
# @brief : entry point for manage service start order
# history : init
#******************************************************************************
: ${SLEEP_SECOND:=2}
wait_for() {
echo Waiting for $1 to listen on $2...
while ! nc -z $1 $2; do echo waiting...; sleep $SLEEP_SECOND; done
}
declare DEPENDS
declare CMD
while getopts "d:c:" arg
do
case $arg in
d)
DEPENDS=$OPTARG
;;
c)
CMD=$OPTARG
;;
?)
echo "unkonw argument"
exit 1
;;
esac
done
for var in ${DEPENDS//,/ }
do
host=${var%:*}
port=${var#*:}
wait_for $host $port
done
eval $CMD

View File

@ -4,12 +4,14 @@ import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import lombok.Setter;
import org.jetlinks.core.metadata.DataType;
import org.jetlinks.core.metadata.types.StringType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@ -23,18 +25,33 @@ public class MeterRegistryManager {
private Map<String, MeterRegistry> meterRegistryMap = new ConcurrentHashMap<>();
@Autowired
private List<MeterRegistrySupplier> suppliers;
private final List<MeterRegistrySupplier> suppliers;
private MeterRegistry createMeterRegistry(String metric, String... tagKeys) {
return new CompositeMeterRegistry(Clock.SYSTEM,
suppliers.stream()
.map(supplier -> supplier.getMeterRegistry(metric, tagKeys))
.collect(Collectors.toList()));
public MeterRegistryManager(@Autowired(required = false) List<MeterRegistrySupplier> suppliers) {
this.suppliers = suppliers == null ? new ArrayList<>() : suppliers;
}
private MeterRegistry createMeterRegistry(String metric, Map<String, DataType> tagDefine) {
Map<String, DataType> tags = new HashMap<>(tagDefine);
MeterRegistrySettings settings = tags::put;
return new CompositeMeterRegistry(Clock.SYSTEM, suppliers
.stream()
.map(supplier -> supplier.getMeterRegistry(metric))
.collect(Collectors.toList()));
}
public MeterRegistry getMeterRegister(String metric, String... tagKeys) {
return meterRegistryMap.computeIfAbsent(metric, _metric -> createMeterRegistry(_metric, tagKeys));
return meterRegistryMap.computeIfAbsent(metric, _metric -> {
if (tagKeys.length == 0) {
return createMeterRegistry(metric, Collections.emptyMap());
}
return createMeterRegistry(metric, Arrays
.stream(tagKeys)
.collect(Collectors.toMap(Function.identity(), key -> StringType.GLOBAL)));
});
}
}

View File

@ -29,58 +29,58 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnBean(ProtocolSupports.class)
public class DeviceClusterConfiguration {
@Bean
public ClusterDeviceRegistry deviceRegistry(ProtocolSupports supports,
ClusterManager manager,
ConfigStorageManager storageManager,
DeviceOperationBroker handler) {
return new ClusterDeviceRegistry(supports,
storageManager,
manager,
handler,
CaffeinatedGuava.build(Caffeine.newBuilder()));
}
@Bean
@ConditionalOnBean(ClusterDeviceRegistry.class)
public BeanPostProcessor interceptorRegister(ClusterDeviceRegistry registry) {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DeviceMessageSenderInterceptor) {
registry.addInterceptor(((DeviceMessageSenderInterceptor) bean));
}
if (bean instanceof DeviceStateChecker) {
registry.addStateChecker(((DeviceStateChecker) bean));
}
return bean;
}
};
}
@Bean(initMethod = "init", destroyMethod = "shutdown")
@ConditionalOnBean(RpcManager.class)
public PersistenceDeviceSessionManager deviceSessionManager(RpcManager rpcManager) {
return new PersistenceDeviceSessionManager(rpcManager);
}
@ConditionalOnBean(DecodedClientMessageHandler.class)
@Bean
public ClusterSendToDeviceMessageHandler defaultSendToDeviceMessageHandler(DeviceSessionManager sessionManager,
DeviceRegistry registry,
MessageHandler messageHandler,
DecodedClientMessageHandler clientMessageHandler) {
return new ClusterSendToDeviceMessageHandler(sessionManager, messageHandler, registry, clientMessageHandler);
}
@Bean
public RpcDeviceOperationBroker rpcDeviceOperationBroker(RpcManager rpcManager,
DeviceSessionManager sessionManager) {
return new RpcDeviceOperationBroker(rpcManager, sessionManager);
}
// @Bean
// public ClusterDeviceRegistry deviceRegistry(ProtocolSupports supports,
// ClusterManager manager,
// ConfigStorageManager storageManager,
// DeviceOperationBroker handler) {
//
// return new ClusterDeviceRegistry(supports,
// storageManager,
// manager,
// handler,
// CaffeinatedGuava.build(Caffeine.newBuilder()));
// }
//
//
// @Bean
// @ConditionalOnBean(ClusterDeviceRegistry.class)
// public BeanPostProcessor interceptorRegister(ClusterDeviceRegistry registry) {
// return new BeanPostProcessor() {
// @Override
// public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// if (bean instanceof DeviceMessageSenderInterceptor) {
// registry.addInterceptor(((DeviceMessageSenderInterceptor) bean));
// }
// if (bean instanceof DeviceStateChecker) {
// registry.addStateChecker(((DeviceStateChecker) bean));
// }
// return bean;
// }
// };
// }
//
// @Bean(initMethod = "init", destroyMethod = "shutdown")
// @ConditionalOnBean(RpcManager.class)
// public PersistenceDeviceSessionManager deviceSessionManager(RpcManager rpcManager) {
//
// return new PersistenceDeviceSessionManager(rpcManager);
// }
//
// @ConditionalOnBean(DecodedClientMessageHandler.class)
// @Bean
// public ClusterSendToDeviceMessageHandler defaultSendToDeviceMessageHandler(DeviceSessionManager sessionManager,
// DeviceRegistry registry,
// MessageHandler messageHandler,
// DecodedClientMessageHandler clientMessageHandler) {
// return new ClusterSendToDeviceMessageHandler(sessionManager, messageHandler, registry, clientMessageHandler);
// }
//
// @Bean
// public RpcDeviceOperationBroker rpcDeviceOperationBroker(RpcManager rpcManager,
// DeviceSessionManager sessionManager) {
// return new RpcDeviceOperationBroker(rpcManager, sessionManager);
// }
}

View File

@ -7,6 +7,7 @@ import org.jetlinks.community.elastic.search.service.ElasticSearchService;
import org.jetlinks.community.elastic.search.things.ElasticSearchColumnModeStrategy;
import org.jetlinks.community.elastic.search.things.ElasticSearchRowModeStrategy;
import org.jetlinks.community.things.data.ThingsDataRepositoryStrategy;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -16,6 +17,7 @@ import org.springframework.context.annotation.Configuration;
public class ElasticSearchThingDataConfiguration {
@Bean
@ConditionalOnBean(ElasticSearchService.class)
public ElasticSearchColumnModeStrategy elasticSearchColumnModThingDataPolicy(
ThingsRegistry registry,
ElasticSearchService searchService,
@ -26,6 +28,7 @@ public class ElasticSearchThingDataConfiguration {
}
@Bean
@ConditionalOnBean(ElasticSearchService.class)
public ElasticSearchRowModeStrategy elasticSearchRowModThingDataPolicy(
ThingsRegistry registry,
ElasticSearchService searchService,

View File

@ -3,6 +3,7 @@ package org.jetlinks.community.elastic.search.index.strategies;
import org.hswebframework.utils.time.DateFormatter;
import org.jetlinks.community.elastic.search.index.ElasticSearchIndexProperties;
import org.jetlinks.community.elastic.search.service.reactive.ReactiveElasticsearchClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
@ -15,6 +16,7 @@ import java.util.Date;
* @since 1.0
*/
@Component
@ConditionalOnBean(ReactiveElasticsearchClient.class)
public class TimeByDayElasticSearchIndexStrategy extends TemplateElasticSearchIndexStrategy {
public TimeByDayElasticSearchIndexStrategy(ReactiveElasticsearchClient client, ElasticSearchIndexProperties properties) {

View File

@ -3,6 +3,7 @@ package org.jetlinks.community.gateway.external.socket;
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
import org.hswebframework.web.authorization.token.UserTokenManager;
import org.jetlinks.community.gateway.external.MessagingManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -16,10 +17,10 @@ import java.util.HashMap;
import java.util.Map;
@Configuration
//@ConditionalOnBean({
// ReactiveAuthenticationManager.class,
// UserTokenManager.class
//})
@ConditionalOnBean({
ReactiveAuthenticationManager.class,
UserTokenManager.class
})
public class WebSocketMessagingHandlerConfiguration {

View File

@ -8,6 +8,7 @@ import org.jetlinks.core.event.Subscription;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -30,6 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/
@Component
@Slf4j
@ConditionalOnBean(NetworkConfigManager.class)
public class DefaultNetworkManager implements NetworkManager, BeanPostProcessor, CommandLineRunner {
private final NetworkConfigManager configManager;

View File

@ -27,6 +27,7 @@
<module>script-component</module>
<module>protocol-component</module>
<module>relation-component</module>
<module>test-component</module>
</modules>
<artifactId>jetlinks-components</artifactId>

View File

@ -6,6 +6,7 @@ import org.jetlinks.community.elastic.search.index.ElasticSearchIndexManager;
import org.jetlinks.community.rule.engine.event.handler.RuleEngineLoggerIndexProvider;
import org.jetlinks.core.metadata.types.DateTimeType;
import org.jetlinks.core.metadata.types.StringType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@ -16,6 +17,7 @@ import org.springframework.stereotype.Component;
@Component
@Order(1)
@Slf4j
@ConditionalOnBean(DefaultElasticSearchIndexMetadata.class)
public class RuleEngineLogIndexInitialize {
public RuleEngineLogIndexInitialize(ElasticSearchIndexManager indexManager) {

View File

@ -2,11 +2,13 @@ package org.jetlinks.community.rule.engine.event.handler;
import lombok.extern.slf4j.Slf4j;
import org.jetlinks.community.elastic.search.service.ElasticSearchService;
import org.jetlinks.community.elastic.search.service.reactive.ReactiveElasticsearchClient;
import org.jetlinks.community.gateway.annotation.Subscribe;
import org.jetlinks.community.rule.engine.entity.RuleEngineExecuteEventInfo;
import org.jetlinks.core.event.TopicPayload;
import org.jetlinks.rule.engine.defaults.LogEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@ -14,6 +16,7 @@ import reactor.core.publisher.Mono;
@Component
@Slf4j
@Order(3)
@ConditionalOnBean(ElasticSearchService.class)
public class RuleLogHandler {
@Autowired

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jetlinks-components</artifactId>
<groupId>org.jetlinks.community</groupId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<description>测试基础模块,引入后请设置scope为test</description>
<artifactId>test-component</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.jetlinks</groupId>
<artifactId>rule-engine-support</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jetlinks</groupId>
<artifactId>jetlinks-supports</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<!-- <scope>test</scope>-->
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-commons-crud</artifactId>
<version>${hsweb.framework.version}</version>
</dependency>
<dependency>
<groupId>org.hswebframework.web</groupId>
<artifactId>hsweb-starter</artifactId>
<version>${hsweb.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.15.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.15.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
<dependency>
<groupId>uk.co.jemos.podam</groupId>
<artifactId>podam</artifactId>
<version>7.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,29 @@
package org.jetlinks.community.test.spring;
import org.hswebframework.web.crud.annotation.EnableEasyormRepository;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@SpringBootApplication(
scanBasePackages = {
"org.jetlinks.community",
},
exclude = {
DataSourceAutoConfiguration.class,
KafkaAutoConfiguration.class,
RabbitAutoConfiguration.class,
ElasticsearchRestClientAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class
}
)
@EnableEasyormRepository("org.jetlinks.**.entity")
public class TestJetLinksApplication {
}

View File

@ -0,0 +1,86 @@
package org.jetlinks.community.test.spring;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.ReactiveAuthenticationHolder;
import org.hswebframework.web.authorization.ReactiveAuthenticationSupplier;
import org.hswebframework.web.crud.configuration.EasyormConfiguration;
import org.hswebframework.web.crud.configuration.R2dbcSqlExecutorConfiguration;
import org.jetlinks.community.test.utils.ContainerUtils;
import org.jetlinks.community.test.web.TestAuthentication;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.core.publisher.Mono;
@ImportAutoConfiguration({
EasyormConfiguration.class,
R2dbcSqlExecutorConfiguration.class, R2dbcAutoConfiguration.class,
R2dbcTransactionManagerAutoConfiguration.class, TransactionAutoConfiguration.class,
WebClientAutoConfiguration.class, RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class
})
@EnableTransactionManagement(proxyTargetClass = true)
@EnableAutoConfiguration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Testcontainers
@ComponentScan("org.jetlinks.community")
public class TestJetLinksController {
static {
System.setProperty("spring.r2dbc.url", "r2dbc:h2:mem:///./data/h2db/jetlinks");
System.setProperty("spring.r2dbc.username", "sa");
}
@Container
static GenericContainer<?> redis = ContainerUtils.newRedis();
@Autowired
protected WebTestClient client;
@BeforeAll
static void initAll() {
System.setProperty("spring.redis.port", String.valueOf(redis.getMappedPort(6379)));
System.setProperty("spring.redis.host", "127.0.0.1");
}
@BeforeEach
void init() {
ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() {
@Override
public Mono<Authentication> get() {
TestAuthentication authentication = new TestAuthentication("test");
initAuth(authentication);
return Mono.just(authentication);
}
@Override
public Mono<Authentication> get(String userId) {
TestAuthentication authentication = new TestAuthentication(userId);
initAuth(authentication);
return Mono.just(authentication);
}
});
}
protected void initAuth(TestAuthentication authentication) {
}
}

View File

@ -0,0 +1,52 @@
package org.jetlinks.community.test.utils;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;
public class ContainerUtils {
public static GenericContainer<?> newRedis() {
return new GenericContainer<>(DockerImageName.parse("redis:5"))
.withEnv("TZ", "Asia/Shanghai")
.withExposedPorts(6379)
.waitingFor(Wait.forListeningPort());
}
public static GenericContainer<?> newMysql() {
return new GenericContainer<>(DockerImageName.parse("mysql:" + System.getProperty("test.mysql.version", "5.7")))
.withEnv("TZ", "Asia/Shanghai")
.withEnv("MYSQL_ROOT_PASSWORD", "password")
.withEnv("MYSQL_DATABASE", "jetlinks")
.withCommand("--character-set-server=utf8mb4")
.withExposedPorts(3306)
.waitingFor(Wait.forListeningPort());
}
public static GenericContainer<?> newPostgresql() {
return new GenericContainer<>(DockerImageName.parse("postgres:" + System.getProperty("test.postgres.version", "11")) + "-alpine")
.withEnv("TZ", "Asia/Shanghai")
.withEnv("POSTGRES_PASSWORD", "password")
.withEnv("POSTGRES_DB", "jetlinks")
.withCommand("postgres", "-c", "max_connections=500")
.withExposedPorts(5432)
.waitingFor(Wait.forListeningPort());
}
public static GenericContainer<?> newElasticSearch() {
return newElasticSearch(System.getProperty("test.elasticsearch.version", "7.9.0"));
}
public static GenericContainer<?> newElasticSearch(String version) {
return new GenericContainer<>(DockerImageName.parse("elasticsearch:" + version))
.withEnv("TZ", "Asia/Shanghai")
.withEnv("ES_JAVA_OPTS","-Djava.net.preferIPv4Stack=true -Xms1g -Xmx1g")
// .withSharedMemorySize(DataSize.ofGigabytes(1).toBytes())
.withEnv("transport.host", "0.0.0.0")
.withEnv("discovery.type", "single-node")
// .withEnv("bootstrap.memory_lock", "true")
.withExposedPorts(9200)
.waitingFor(Wait.forHttp("/").forStatusCode(200));
}
}

View File

@ -0,0 +1,297 @@
package org.jetlinks.pro.test.web;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.time.Duration;
import java.util.function.Function;
import lombok.SneakyThrows;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.test.web.client.*;
import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseCookie.ResponseCookieBuilder;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.mock.http.client.reactive.MockClientHttpRequest;
import org.springframework.mock.http.client.reactive.MockClientHttpResponse;
import org.springframework.test.web.client.match.MockRestRequestMatchers;
import org.springframework.test.web.client.response.MockRestResponseCreators;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
/**
* A mock REST service server that can be used to verify requests from {@link WebClient} objects, and
* stub mock responses to provide to the client.
* <p>
* This acts as a drop-in replacement for {@link MockRestServiceServer} that provides support for reactive
* {@link WebClient} objects. While supporting a reactive client API underneath, the majority of this implementation
* works with the same components that {@link MockRestServiceServer} does.
* <p>
* This is relatively lightweight API to use, so can be initialized per test case if required. This does not open
* any network sockets.
* <p>
* Instances of this server can be created by calling either
* {@link #createServer()}, {@link #createServer(boolean)}, or {@link #createServer(RequestExpectationManager)}.
* {@link WebClient} instances that will communicate with this server can then be created using the
* {@link #createWebClient()} or {@link #createWebClientBuilder()} methods, respectively.
*
* @author Ashley Scopes
* @see RequestMatcher
* @see ResponseCreator
* @see MockRestRequestMatchers
* @see MockRestResponseCreators
*/
public final class MockReactiveRestServiceServer {
private final RequestExpectationManager expectationManager;
static DefaultDataBufferFactory bufferFactory=new DefaultDataBufferFactory();
/**
* Initialize the mock server with the desired expectation manager to use.
*
* @param expectationManager the expectation manager to use for requests.
*/
private MockReactiveRestServiceServer(@NonNull RequestExpectationManager expectationManager) {
this.expectationManager = expectationManager;
}
/**
* Register an expectation for a request to occur exactly once. This is an alias for calling
* {@link #expect(ExpectedCount, RequestMatcher)} with {@link ExpectedCount#once()} as the first
* parameter.
*
* @param requestMatcher the request matcher to use.
* @return the response actions builder to use to define how to respond to such requests.
*/
@NonNull
public ResponseActions expect(@NonNull RequestMatcher requestMatcher) {
return expect(ExpectedCount.once(), requestMatcher);
}
/**
* Register an expectation for a request to occur a given number of times.
*
* @param count the number of times the request should be made.
* @param matcher the matcher for the request.
* @return the response actions builder to use to define how to respond to such requests.
*/
@NonNull
public ResponseActions expect(@NonNull ExpectedCount count, @NonNull RequestMatcher matcher) {
return this.expectationManager.expectRequest(count, matcher);
}
/**
* Verify that the expected requests have been performed. This is equivalent to calling
* {@link #verify(Duration)} with {@link Duration#ZERO}.
*
* @throws AssertionError if the requests have not been performed as expected.
*/
public void verify() {
this.expectationManager.verify();
}
/**
* Verify that the expected results get performed within the given timeout.
*
* @param timeout the maximum time to wait before failing.
* @throws AssertionError if the request times out before the expected requests occur.
*/
@NonNull
public void verify(@NonNull Duration timeout) {
this.expectationManager.verify();
}
/**
* Remove all expectations from this mock server.
*/
public void reset() {
this.expectationManager.reset();
}
/**
* Create a new {@link WebClient} builder that is bound to this mock server.
*
* @return a {@code WebClient} builder that will pipe requests through this mock server instance.
*/
@NonNull
public WebClient.Builder createWebClientBuilder() {
return WebClient
.builder()
.clientConnector(new MockClientHttpConnector());
}
/**
* Create a new {@link WebClient} that is bound to this mock server.
*
* @return a {@code WebClient} with default settings that pipes requests through this mock server instance.
*/
@NonNull
public WebClient createWebClient() {
return createWebClientBuilder().build();
}
/**
* Create a new mock reactive REST service server with the given expectation manager.
*
* @param manager the expectation manager to use.
* @return the new mock reactive REST service server.
*/
@NonNull
public static MockReactiveRestServiceServer createServer(@NonNull RequestExpectationManager manager) {
return new MockReactiveRestServiceServer(manager);
}
/**
* Create a new mock reactive REST service server.
*
* @param ignoreRequestOrder true to ignore the order of requests, or false to require requests to
* be performed in the order that they are registered.
* @return the new mock reactive REST service server.
*/
@NonNull
public static MockReactiveRestServiceServer createServer(boolean ignoreRequestOrder) {
RequestExpectationManager manager = ignoreRequestOrder
? new UnorderedRequestExpectationManager()
: new SimpleRequestExpectationManager();
return createServer(manager);
}
/**
* Create a new mock reactive REST service server that requires requests to be performed in the order that they
* are registered.
*
* @return the new mock reactive REST service server to use.
*/
@NonNull
public static MockReactiveRestServiceServer createServer() {
return createServer(false);
}
/**
* Internal mock connector for {@link WebClient} instances. This deals with converting reactive
* {@link org.springframework.mock.http.client.reactive.MockClientHttpRequest} objects to non-reactive
* {@link org.springframework.mock.http.client.MockClientHttpRequest} objects that the
* {@link MockRestServiceServer} internals are compatible with. This also will handle converting non-reactive
* {@link org.springframework.mock.http.client.MockClientHttpResponse} objects back to reactive
* {@link org.springframework.mock.http.client.reactive.MockClientHttpResponse} objects for the response object
* to use internally.
*
* @author Ashley Scopes
*/
private final class MockClientHttpConnector implements ClientHttpConnector {
@NonNull
public Mono<ClientHttpResponse> connect(
@NonNull HttpMethod method, @NonNull URI uri,
@NonNull Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
MockClientHttpRequest request = new MockClientHttpRequest(method, uri);
return Mono
.defer(() -> requestCallback.apply(request))
.thenReturn(request)
.flatMap(this::createRequest)
.map(this::performRequest)
.map(this::convertResponse)
.checkpoint("Request to " + method + " " + uri + " [MockReactiveRestServiceServer]")
.onErrorMap(ex -> ex.getCause() instanceof AssertionError
? Exceptions.bubble(ex.getCause())
: ex);
}
@NonNull
private Mono<org.springframework.http.client.ClientHttpRequest> createRequest(
@NonNull MockClientHttpRequest reactiveRequest) {
org.springframework.mock.http.client.MockClientHttpRequest proceduralRequest =
new org.springframework.mock.http.client.MockClientHttpRequest(
reactiveRequest.getMethod(), reactiveRequest.getURI());
proceduralRequest.getHeaders().putAll(reactiveRequest.getHeaders());
return DataBufferUtils
.join(reactiveRequest.getBody())
.doOnNext(dataBuffer -> {
byte[] arr = new byte[1024];
int c;
try {
InputStream input = dataBuffer.asInputStream();
OutputStream output = proceduralRequest.getBody();
while ((c = input.read(arr)) != -1) {
output.write(arr, 0, c);
}
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
})
.thenReturn(proceduralRequest);
}
@NonNull
@SneakyThrows
private org.springframework.http.client.ClientHttpResponse performRequest(
@NonNull org.springframework.http.client.ClientHttpRequest proceduralRequest) {
return MockReactiveRestServiceServer
.this
.expectationManager
.validateRequest(proceduralRequest);
}
@NonNull
private ClientHttpResponse convertResponse(
@NonNull org.springframework.http.client.ClientHttpResponse proceduralResponse) {
int status = -1;
String statusMessage = "Could not read response status";
HttpHeaders headers = null;
try {
status = proceduralResponse.getRawStatusCode();
statusMessage = proceduralResponse.getStatusText();
headers = proceduralResponse.getHeaders();
DataBuffer responseBody = bufferFactory
.wrap(IOUtils.toByteArray(proceduralResponse.getBody()));
MockClientHttpResponse reactiveResponse = new MockClientHttpResponse(status);
reactiveResponse.getHeaders().putAll(headers);
MultiValueMap<String, ResponseCookie> cookies = reactiveResponse.getCookies();
headers.getOrEmpty(HttpHeaders.SET_COOKIE)
.stream()
.filter(cookie -> cookie.contains("="))
.map(cookie -> ResponseCookie.fromClientResponse(
cookie.substring(0, cookie.indexOf('=')),
cookie.substring(cookie.indexOf('=') + 1)
))
.map(ResponseCookieBuilder::build)
.forEach(cookie -> cookies.add(cookie.getName(), cookie));
reactiveResponse.setBody(Flux.just(responseBody));
return reactiveResponse;
}
catch (IOException ex) {
throw new WebClientResponseException(status, statusMessage, headers, null, null);
}
}
}
}

View File

@ -0,0 +1,83 @@
package org.jetlinks.community.test.web;
import lombok.Getter;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.Dimension;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.User;
import org.hswebframework.web.authorization.simple.SimpleDimension;
import org.hswebframework.web.authorization.simple.SimpleDimensionType;
import org.hswebframework.web.authorization.simple.SimplePermission;
import org.hswebframework.web.authorization.simple.SimpleUser;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
@Getter
public class TestAuthentication implements Authentication {
private final User user;
private final List<Dimension> dimensions = new ArrayList<>();
private final List<Permission> permissions = new ArrayList<>();
public TestAuthentication(String userId) {
this.user = new SimpleUser(userId, userId, "test", "test", null);
}
public TestAuthentication addDimension(String type, String id) {
return addDimension(type,id,null);
}
public TestAuthentication addDimension(String type, String id,Map<String,Object> options) {
dimensions.add(SimpleDimension.of(id, type, SimpleDimensionType.of(type), options));
return this;
}
public TestAuthentication addTenant(String tenantId,boolean admin) {
Map<String,Object> options = new HashMap<>();
options.put("tenantId",tenantId);
options.put("admin",admin);
dimensions.add(SimpleDimension.of(tenantId, tenantId, SimpleDimensionType.of("tenantMember"), options));
return this;
}
public TestAuthentication addPermission(String id, String... action) {
permissions.add(SimplePermission.builder()
.id(id)
.name(id).actions(new HashSet<>(Arrays.asList(action)))
.build());
return this;
}
@Override
public <T extends Serializable> Optional<T> getAttribute(String name) {
return Optional.empty();
}
@Override
public Map<String, Serializable> getAttributes() {
return null;
}
@Override
public Authentication merge(Authentication source) {
return this;
}
@Override
public Authentication copy(BiPredicate<Permission, String> permissionFilter,
Predicate<Dimension> dimension) {
return this;
}
}

View File

@ -0,0 +1,36 @@
package org.jetlinks.community.test.utils;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Testcontainers
class ContainerUtilsTest {
@Container
GenericContainer<?> redis = ContainerUtils.newRedis();
@Container
GenericContainer<?> mysql = ContainerUtils.newMysql();
@Container
GenericContainer<?> postgresql = ContainerUtils.newPostgresql();
@Container
GenericContainer<?> elasticsearch = ContainerUtils.newElasticSearch();
@Test
void test(){
assertTrue(redis.isRunning());
assertTrue(mysql.isRunning());
assertTrue(postgresql.isRunning());
assertTrue(elasticsearch.isRunning());
}
}

View File

@ -0,0 +1,76 @@
package org.jetlinks.community.timeseries;
import org.hswebframework.ezorm.core.param.QueryParam;
import org.jetlinks.community.timeseries.query.AggregationData;
import org.jetlinks.community.timeseries.query.AggregationQueryParam;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collection;
public class NoneTimeSeriesManager implements TimeSeriesManager {
public static final NoneTimeSeriesService NONE = new NoneTimeSeriesService();
@Override
public TimeSeriesService getService(TimeSeriesMetric metric) {
return NONE;
}
@Override
public TimeSeriesService getServices(TimeSeriesMetric... metric) {
return NONE;
}
@Override
public TimeSeriesService getServices(String... metric) {
return NONE;
}
@Override
public TimeSeriesService getService(String metric) {
return NONE;
}
@Override
public Mono<Void> registerMetadata(TimeSeriesMetadata metadata) {
return Mono.empty();
}
static class NoneTimeSeriesService implements TimeSeriesService {
@Override
public Flux<TimeSeriesData> query(QueryParam queryParam) {
return Flux.empty();
}
@Override
public Flux<TimeSeriesData> multiQuery(Collection<QueryParam> query) {
return Flux.empty();
}
@Override
public Mono<Integer> count(QueryParam queryParam) {
return Mono.empty();
}
@Override
public Flux<AggregationData> aggregation(AggregationQueryParam queryParam) {
return Flux.empty();
}
@Override
public Mono<Void> commit(Publisher<TimeSeriesData> data) {
return Mono.empty();
}
@Override
public Mono<Void> commit(TimeSeriesData data) {
return Mono.empty();
}
@Override
public Mono<Void> save(Publisher<TimeSeriesData> data) {
return Mono.empty();
}
}
}

View File

@ -0,0 +1,18 @@
package org.jetlinks.community.timeseries;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Flux;
import java.time.Duration;
@Configuration(proxyBeanMethods = false)
public class TimeSeriesManagerConfiguration {
@ConditionalOnMissingBean(TimeSeriesManager.class)
@Bean
public NoneTimeSeriesManager timeSeriesManager() {
return new NoneTimeSeriesManager();
}
}

View File

@ -76,6 +76,13 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetlinks.community</groupId>
<artifactId>test-component</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,22 @@
package org.jetlinks.community.auth;
import org.hswebframework.web.authorization.simple.DefaultAuthorizationAutoConfiguration;
import org.hswebframework.web.starter.jackson.CustomCodecsAutoConfiguration;
import org.hswebframework.web.system.authorization.defaults.configuration.AuthorizationServiceAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Configuration;
@Configuration
@ImportAutoConfiguration({
AuthorizationServiceAutoConfiguration.class,
CodecsAutoConfiguration.class,
JacksonAutoConfiguration.class,
CustomCodecsAutoConfiguration.class,
DefaultAuthorizationAutoConfiguration.class
})
public class AuthenticationTestConfiguration {
}

View File

@ -0,0 +1,88 @@
package org.jetlinks.community.auth.relation;
import org.hswebframework.ezorm.core.StaticMethodReferenceColumn;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.ezorm.rdb.mapping.defaults.DefaultReactiveQuery;
import org.jetlinks.community.auth.entity.ThirdPartyUserBindEntity;
import org.jetlinks.community.auth.entity.UserDetail;
import org.jetlinks.community.auth.service.UserDetailService;
import org.jetlinks.community.relation.RelationConstants;
import org.jetlinks.core.things.relation.PropertyOperation;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
class UserRelationObjectProviderTest {
@Test
void test() {
UserDetailService service = Mockito.mock(UserDetailService.class);
ReactiveRepository<ThirdPartyUserBindEntity, String> repository = Mockito.mock(ReactiveRepository.class);
UserDetail detail = new UserDetail();
detail.setName("Admin");
detail.setUsername("admin");
detail.setId("admin");
detail.setEmail("admin@hsweb.me");
{
Mockito.when(service.findUserDetail("admin"))
.thenReturn(Mono.just(detail));
}
ThirdPartyUserBindEntity entity = new ThirdPartyUserBindEntity();
entity.setType("wx");
entity.setThirdPartyUserId("third-admin");
entity.setProvider("dingtalk");
entity.setUserId("admin");
{
DefaultReactiveQuery query = Mockito.mock(DefaultReactiveQuery.class);
Mockito.when(query.where(Mockito.any(StaticMethodReferenceColumn.class),Mockito.any()))
.thenReturn(query);
Mockito.when(query.and(Mockito.any(StaticMethodReferenceColumn.class),Mockito.any()))
.thenReturn(query);
Mockito.when(query.fetch())
.thenReturn(Flux.just(
entity
));
Mockito.when(repository.createQuery())
.thenReturn(query);
}
UserRelationObjectProvider provider = new UserRelationObjectProvider(service, repository);
PropertyOperation operation = provider.properties("admin");
operation.get(RelationConstants.UserProperty.name)
.as(StepVerifier::create)
.expectNext(detail.getName())
.verifyComplete();
operation.get(RelationConstants.UserProperty.email)
.as(StepVerifier::create)
.expectNext(detail.getEmail())
.verifyComplete();
operation.get("null")
.as(StepVerifier::create)
.expectComplete()
.verify();
operation.get(RelationConstants.UserProperty.thirdParty(entity.getType(),entity.getProvider()))
.as(StepVerifier::create)
.expectNext(entity.getThirdPartyUserId())
.verifyComplete();
}
}

View File

@ -0,0 +1,151 @@
package org.jetlinks.community.auth.service;
import org.hswebframework.ezorm.core.StaticMethodReferenceColumn;
import org.hswebframework.ezorm.core.param.QueryParam;
import org.hswebframework.ezorm.rdb.mapping.ReactiveDelete;
import org.hswebframework.ezorm.rdb.mapping.ReactiveQuery;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
import org.hswebframework.web.crud.events.EntityDeletedEvent;
import org.hswebframework.web.system.authorization.api.entity.DimensionUserEntity;
import org.hswebframework.web.system.authorization.defaults.service.DefaultDimensionUserService;
import org.jetlinks.community.auth.entity.OrganizationEntity;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
/**
* 输入描述.
*
* @author zhangji
* @version 1.11 2021/10/19
*/
public class OrganizationServiceTest {
public static final String ORG_ID = "org-test";
public static final String TENANT_ID_1 = "tenant-1";
public static final String TENANT_ID_2 = "tenant-2";
public static final String USER_ID_1 = "user-1";
public static final String USER_ID_2 = "user-2";
public static final String USER_ID_3 = "user-3";
@Test
void test() {
// 模拟DefaultDimensionUserService
ReactiveRepository<DimensionUserEntity, String> dimensionUserrepository = Mockito.mock(ReactiveRepository.class);
ReactiveDelete reactiveDelete = Mockito.mock(ReactiveDelete.class);
ReactiveQuery<DimensionUserEntity> dimensionUserQuery = Mockito.mock(ReactiveQuery.class);
Mockito.when(dimensionUserrepository.save(Mockito.any(Publisher.class)))
.thenReturn(Mono.just(SaveResult.of(3, 0)));
Mockito.when(dimensionUserrepository.createDelete())
.thenReturn(reactiveDelete);
Mockito.when(reactiveDelete.where(Mockito.any(StaticMethodReferenceColumn.class), Mockito.any()))
.thenReturn(reactiveDelete);
Mockito.when(reactiveDelete.in(Mockito.any(StaticMethodReferenceColumn.class), Mockito.anyCollection()))
.thenReturn(reactiveDelete);
Mockito.when(reactiveDelete.and(Mockito.any(StaticMethodReferenceColumn.class), Mockito.any()))
.thenReturn(reactiveDelete);
Mockito.when(reactiveDelete.execute())
.thenReturn(Mono.just(1));
Mockito.when(reactiveDelete.onExecute(Mockito.any(BiFunction.class)))
.thenReturn(reactiveDelete);
Mockito.when(dimensionUserrepository.createQuery())
.thenReturn(dimensionUserQuery);
Mockito.when(dimensionUserQuery.select(Mockito.any(StaticMethodReferenceColumn.class)))
.thenReturn(dimensionUserQuery);
Mockito.when(dimensionUserQuery.setParam(Mockito.any(QueryParam.class)))
.thenReturn(dimensionUserQuery);
Mockito.when(dimensionUserQuery.fetch())
.thenReturn(Flux.fromIterable(dimensionUserEntityList()));
DefaultDimensionUserService dimensionUserService = new DefaultDimensionUserService() {
@Override
public ReactiveRepository<DimensionUserEntity, String> getRepository() {
return dimensionUserrepository;
}
@Override
public Mono<SaveResult> save(Publisher<DimensionUserEntity> entityPublisher) {
return Flux.from(entityPublisher)
.count()
.map(Long::intValue)
.map(i -> SaveResult.of(i, 0));
}
};
// 模拟repostory
ReactiveRepository<OrganizationEntity, String> repository = Mockito.mock(ReactiveRepository.class);
Mockito.when(repository.findById(Mockito.anyString()))
.thenReturn(Mono.just(organizationEntity()));
// 构建OrganizationService
OrganizationService organizationService = new OrganizationService(dimensionUserService) {
@Override
public ReactiveRepository<OrganizationEntity, String> getRepository() {
return repository;
}
};
// 绑定用户
organizationService.bindUser(
ORG_ID, Arrays.asList(USER_ID_1, USER_ID_2, USER_ID_3))
.as(StepVerifier::create)
.expectNext(3)
.verifyComplete();
// 绑定用户-不指定租户ID
organizationService.bindUser(ORG_ID, Arrays.asList(USER_ID_1, USER_ID_2, USER_ID_3))
.as(StepVerifier::create)
.expectNext(3)
.verifyComplete();
// 解绑用户
organizationService.unbindUser(ORG_ID, Arrays.asList(USER_ID_1))
.as(StepVerifier::create)
.expectNext(1)
.verifyComplete();
// 解绑用户-不指定租户ID
organizationService.unbindUser(ORG_ID, Arrays.asList(USER_ID_1))
.as(StepVerifier::create)
.expectNext(1)
.verifyComplete();
EntityDeletedEvent<OrganizationEntity> event = new EntityDeletedEvent<>(
Arrays.asList(organizationEntity()), OrganizationEntity.class
);
event.getAsync()
.as(StepVerifier::create)
.expectComplete();
}
private OrganizationEntity organizationEntity() {
OrganizationEntity organizationEntity = new OrganizationEntity();
organizationEntity.setId(ORG_ID);
organizationEntity.setName("测试机构");
return organizationEntity;
}
private List<DimensionUserEntity> dimensionUserEntityList() {
DimensionUserEntity dimensionUserEntity1 = new DimensionUserEntity();
dimensionUserEntity1.setId("1");
dimensionUserEntity1.setDimensionId(TENANT_ID_1);
dimensionUserEntity1.setUserId("test");
DimensionUserEntity dimensionUserEntity2 = new DimensionUserEntity();
dimensionUserEntity2.setId("2");
dimensionUserEntity2.setDimensionId(TENANT_ID_2);
dimensionUserEntity2.setUserId("test");
return Arrays.asList(dimensionUserEntity1, dimensionUserEntity2);
}
}

View File

@ -0,0 +1,34 @@
package org.jetlinks.community.auth.service.terms;
import org.hswebframework.ezorm.core.param.Term;
import org.hswebframework.ezorm.rdb.executor.SqlRequest;
import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ThirdPartyUserTermBuilderTest {
@Test
void test() {
ThirdPartyUserTermBuilder builder = new ThirdPartyUserTermBuilder();
RDBColumnMetadata column = new RDBColumnMetadata();
column.setName("id");
Term term = new Term();
term.setColumn("id");
term.getOptions().add("wx");
term.setValue("providerId");
SqlRequest request = builder
.createFragments("id", column, term)
.toRequest();
System.out.printf(request.toNativeSql());
assertEquals(
"exists(select 1 from s_third_party_user_bind _bind where _bind.user_id = id and _bind.type = 'wx' and _bind.provider in ( 'providerId' ) )",
request.toNativeSql());
assertArrayEquals(request.getParameters(), new Object[]{"wx", "providerId"});
}
}

View File

@ -0,0 +1,73 @@
package org.jetlinks.community.auth.web;
import org.jetlinks.community.auth.web.request.AuthorizationSettingDetail;
import org.jetlinks.community.test.spring.TestJetLinksController;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import reactor.core.publisher.Flux;
import java.util.Arrays;
import java.util.HashSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* 输入描述.
*
* @author zhangji
* @version 1.11 2021/9/28
*/
@DisplayName("权限分配AuthorizationSettingDetailController")
@WebFluxTest(AuthorizationSettingDetailController.class)
class AuthorizationSettingDetailControllerTest extends TestJetLinksController {
public static final String BASE_URI = "/autz-setting/detail";
public static final String TARGET_TYPE = "user";
public static final String TARGET = "test-operator";
@Test
void test() {
Boolean result = client.post()
.uri(BASE_URI + "/_save")
.body(Flux.just(authSettingDetail()), AuthorizationSettingDetail.class)
.exchange()
.expectBody(Boolean.class)
.returnResult()
.getResponseBody();
assertEquals(true, result);
AuthorizationSettingDetail authSettingDetail = client.get()
.uri(BASE_URI + String.format("/%s/%s", TARGET_TYPE, TARGET))
.exchange()
.expectBody(AuthorizationSettingDetail.class)
.returnResult()
.getResponseBody();
assertNotNull(authSettingDetail);
assertEquals(TARGET_TYPE, authSettingDetail.getTargetType());
assertEquals(TARGET, authSettingDetail.getTargetId());
}
AuthorizationSettingDetail authSettingDetail() {
AuthorizationSettingDetail authSettingDetail = new AuthorizationSettingDetail();
authSettingDetail.setTargetType(TARGET_TYPE);
authSettingDetail.setTargetId(TARGET);
authSettingDetail.setMerge(true);
authSettingDetail.setPriority(1);
authSettingDetail.setPermissionList(Arrays.asList(
AuthorizationSettingDetail.PermissionInfo.of("1", new HashSet<>(Arrays.asList("query"))),
AuthorizationSettingDetail.PermissionInfo.of("2", new HashSet<>(Arrays.asList("query", "update")))
));
return authSettingDetail;
}
// @Override
// protected void initAuth(TestAuthentication authentication) {
// super.initAuth(authentication);
// authentication.addDimension(TARGET_TYPE, TARGET);
// }
}

View File

@ -0,0 +1,266 @@
package org.jetlinks.community.auth.web;
import org.hswebframework.web.system.authorization.api.entity.PermissionEntity;
import org.hswebframework.web.system.authorization.api.service.reactive.ReactiveUserService;
import org.hswebframework.web.system.authorization.defaults.service.DefaultPermissionService;
import org.jetlinks.community.auth.entity.MenuButtonInfo;
import org.jetlinks.community.auth.entity.MenuEntity;
import org.jetlinks.community.auth.entity.MenuView;
import org.jetlinks.community.auth.entity.PermissionInfo;
import org.jetlinks.community.auth.service.request.MenuGrantRequest;
import org.jetlinks.community.auth.web.request.AuthorizationSettingDetail;
import org.jetlinks.community.test.spring.TestJetLinksController;
import org.jetlinks.community.test.web.TestAuthentication;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.http.MediaType;
import reactor.test.StepVerifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@WebFluxTest(value = MenuController.class, properties = {
"menu.allow-all-menus-users=test"
})
class MenuControllerTest extends TestJetLinksController {
@Autowired
private DefaultPermissionService permissionService;
@Autowired
private ReactiveUserService userService;
@Test
void testCrud() {
MenuEntity sys = new MenuEntity();
sys.setName("系统设置");
sys.setUrl("/xxx");
sys.setId("sys-crud");
MenuEntity menu = new MenuEntity();
menu.setId("menu-crud");
menu.setName("菜单管理");
menu.setUrl("/xxx");
menu.setParentId(sys.getId());
menu.setButtons(Arrays.asList(
MenuButtonInfo.of("create", "创建菜单", "menu", "add"),
MenuButtonInfo.of("delete", "删除菜单", "menu", "remove")
));
sys.setChildren(Collections.singletonList(menu));
client.patch()
.uri("/menu")
.bodyValue(sys)
.exchange()
.expectStatus()
.is2xxSuccessful();
client.delete()
.uri("/menu/{id}", sys.getId())
.exchange()
.expectStatus()
.is2xxSuccessful();
client.get()
.uri("/menu/_query/no-paging")
.exchange()
.expectBodyList(MenuEntity.class)
.hasSize(0);
}
@Test
void testGrant() {
MenuEntity sys = new MenuEntity();
sys.setName("系统设置");
sys.setUrl("/xxx");
sys.setId("sys");
MenuEntity menu = new MenuEntity();
menu.setId("menu");
menu.setName("菜单管理");
menu.setUrl("/xxx");
menu.setParentId(sys.getId());
menu.setButtons(Arrays.asList(
MenuButtonInfo.of("create", "创建菜单", "menu", "add"),
MenuButtonInfo.of("delete", "删除菜单", "menu", "remove")
));
sys.setChildren(Collections.singletonList(menu));
client.patch()
.uri("/menu")
.bodyValue(sys)
.exchange()
.expectStatus()
.is2xxSuccessful();
MenuGrantRequest request = new MenuGrantRequest();
request.setTargetType("user");
request.setTargetId("test2");
MenuView menuView = new MenuView();
menuView.setId(menu.getId());
menuView.setButtons(Arrays.asList(MenuView.ButtonView.of("create", "创建菜单", "", null)));
menuView.setGranted(true);
request.setMenus(Arrays.asList(menuView));
client
.put()
.uri("/menu/user/test2/_grant")
.bodyValue(request)
.exchange()
.expectStatus()
.is2xxSuccessful();
List<MenuView> tree = client
.get()
.uri("/menu/user/test2/_grant/tree")
.exchange()
.expectBodyList(MenuView.class)
.returnResult()
.getResponseBody();
assertNotNull(tree);
assertFalse(tree.isEmpty());
assertEquals(tree.get(0).getId(), sys.getId());
assertEquals(1, tree.get(0).getChildren().size());
assertEquals(menu.getId(), tree.get(0).getChildren().get(0).getId());
assertEquals(2, tree.get(0).getChildren().get(0).getButtons().size());
assertTrue(tree
.get(0)
.getChildren()
.get(0)
.getButton("create")
.map(MenuView.ButtonView::isGranted)
.orElse(false));
assertFalse(tree
.get(0)
.getChildren()
.get(0)
.getButton("delete")
.map(MenuView.ButtonView::isGranted)
.orElse(true));
client.delete()
.uri("/menu/{id}", sys.getId())
.exchange()
.expectStatus()
.is2xxSuccessful();
}
@Test
void testAssetTypes() {
//菜单
MenuEntity sys = new MenuEntity();
sys.setName("系统设置");
sys.setUrl("/xxx");
sys.setPermissions(Arrays.asList(PermissionInfo.of("test", new HashSet<>(Arrays.asList("save")))));
sys.setId("asset-test");
client.patch()
.uri("/menu")
.bodyValue(sys)
.exchange()
.expectStatus()
.is2xxSuccessful();
//权限
PermissionEntity permissionEntity = new PermissionEntity();
permissionEntity.setId("test");
permissionEntity.setName("Test");
permissionEntity.setActions(new ArrayList<>());
permissionService
.save(permissionEntity)
.then()
.as(StepVerifier::create)
.expectComplete()
.verify();
//资产
List<MenuController.AssetTypeView> view = client
.post()
.uri("/menu/asset-types")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("[{\"id\":\"asset-test\"}]")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBodyList(MenuController.AssetTypeView.class)
.returnResult()
.getResponseBody();
assertNotNull(view);
assertEquals(1, view.size());
}
@Test
void testPermissions() {
//菜单
MenuEntity sys = new MenuEntity();
sys.setName("系统设置");
sys.setUrl("/xxx");
sys.setPermissions(Arrays.asList(PermissionInfo.of("test", new HashSet<>(Arrays.asList("save")))));
sys.setId("asset-test");
client.patch()
.uri("/menu")
.bodyValue(sys)
.exchange()
.expectStatus()
.is2xxSuccessful();
//权限
PermissionEntity permissionEntity = new PermissionEntity();
permissionEntity.setId("test");
permissionEntity.setName("Test");
permissionEntity.setActions(new ArrayList<>());
permissionService
.save(permissionEntity)
.then()
.as(StepVerifier::create)
.expectComplete()
.verify();
List<AuthorizationSettingDetail.PermissionInfo> view = client
.post()
.uri("/menu/permissions")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("[{\"id\":\"asset-test\"}]")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBodyList(AuthorizationSettingDetail.PermissionInfo.class)
.returnResult()
.getResponseBody();
assertNotNull(view);
assertEquals(1, view.size());
assertEquals("test", view.get(0).getId());
}
@Override
protected void initAuth(TestAuthentication authentication) {
super.initAuth(authentication);
authentication.addPermission("menu", "add");
}
}

View File

@ -0,0 +1,201 @@
package org.jetlinks.community.auth.web;
import org.apache.commons.collections.CollectionUtils;
import org.jetlinks.community.auth.entity.OrganizationEntity;
import org.jetlinks.community.test.spring.TestJetLinksController;
import org.jetlinks.community.test.web.TestAuthentication;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.core.ParameterizedTypeReference;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
/**
* 机构管理.
*
* @author zhangji
* @version 1.11 2021/9/28
*/
@DisplayName("机构管理OrganizationController")
@WebFluxTest(OrganizationController.class)
class OrganizationControllerTest extends TestJetLinksController {
public static final String BASE_URI = "/organization";
@Override
protected void initAuth(TestAuthentication authentication) {
super.initAuth(authentication);
}
@Test
void test() {
// 创建
for (OrganizationEntity organizationEntity : orgList()) {
client.post()
.uri(BASE_URI)
.bodyValue(organizationEntity)
.exchange()
.expectStatus()
.is2xxSuccessful();
}
// 查询全部-树结构
List<OrganizationEntity> orgTreeList = client
.get()
.uri(BASE_URI + "/_all/tree")
.exchange()
.expectBodyList(OrganizationEntity.class)
.returnResult()
.getResponseBody();
assertFalse(CollectionUtils.isEmpty(orgTreeList));
OrganizationEntity top = orgTreeList.get(0);
assertEquals("1", top.getId());
List<OrganizationEntity> children = top.getChildren();
assertFalse(CollectionUtils.isEmpty(children));
assertEquals("2", children.get(0).getId());
assertEquals("3", children.get(1).getId());
assertFalse(CollectionUtils.isEmpty(children.get(0).getChildren()));
assertEquals("4", children.get(0).getChildren().get(0).getId());
// 查询全部-列表
// 根据sortIndex字段排序
List<OrganizationEntity> orgList = client
.get()
.uri(BASE_URI + "/_all")
.exchange()
.expectBodyList(OrganizationEntity.class)
.returnResult()
.getResponseBody();
assertFalse(CollectionUtils.isEmpty(orgList));
assertEquals("1", orgList.get(0).getId());
assertEquals("2", orgList.get(1).getId());
assertEquals("3", orgList.get(2).getId());
assertEquals("4", orgList.get(3).getId());
// 查询机构列表(包含子机构)树结构
List<OrganizationEntity> orgIncludeChildrenTreeList = client
.get()
.uri(BASE_URI + "/_query/_children/tree?where=code = 1001&orgerBy=id")
.exchange()
.expectBodyList(OrganizationEntity.class)
.returnResult()
.getResponseBody();
assertFalse(CollectionUtils.isEmpty(orgIncludeChildrenTreeList));
assertEquals(2, orgIncludeChildrenTreeList.get(0).getChildren().size());
// 查询机构列表(包含子机构)
List<OrganizationEntity> orgIncludeChildrenList = client
.get()
.uri(BASE_URI + "/_query/_children?where=code = 1001&orgerBy=id")
.exchange()
.expectBodyList(OrganizationEntity.class)
.returnResult()
.getResponseBody();
assertFalse(CollectionUtils.isEmpty(orgIncludeChildrenList));
assertEquals(4, orgIncludeChildrenList.size());
// 绑定用户到机构
client.post()
.uri(BASE_URI + "/{id}/users/_bind", "1")
.body(Flux.just("test").collectList(), new ParameterizedTypeReference<List<String>>() {
})
.exchange()
.expectStatus()
.is2xxSuccessful();
// 从机构解绑用户
client.post()
.uri(BASE_URI + "/{id}/users/_unbind", "1")
.body(Flux.just("test").collectList(), new ParameterizedTypeReference<List<String>>() {
})
.exchange()
.expectStatus()
.is2xxSuccessful();
client.delete()
.uri(BASE_URI + "/{id}", "1")
.exchange()
.expectStatus()
.is2xxSuccessful();
}
List<OrganizationEntity> orgList() {
List<OrganizationEntity> orgList = new ArrayList<>();
OrganizationEntity orgEntity = new OrganizationEntity();
orgEntity.setId("1");
orgEntity.setCode("1001");
orgEntity.setName("总部");
orgEntity.setType("默认");
orgEntity.setDescribe("组织的最上级");
Map<String, Object> properties = new HashMap<>();
properties.put("province", "北京");
properties.put("alias", "top");
orgEntity.setProperties(properties);
orgEntity.setParentId("");
orgEntity.setPath("1001");
orgEntity.setLevel(1);
orgEntity.setSortIndex(1L);
orgList.add(orgEntity);
OrganizationEntity orgChildrenEntity1 = new OrganizationEntity();
orgChildrenEntity1.setId("2");
orgChildrenEntity1.setCode("1002");
orgChildrenEntity1.setName("重庆分部");
orgChildrenEntity1.setType("默认");
Map<String, Object> childrenProperties1 = new HashMap<>();
childrenProperties1.put("province", "重庆");
childrenProperties1.put("alias", "child-1");
orgChildrenEntity1.setProperties(childrenProperties1);
orgChildrenEntity1.setParentId("1");
orgChildrenEntity1.setPath("1001-1002");
orgChildrenEntity1.setLevel(2);
orgChildrenEntity1.setSortIndex(2L);
orgList.add(orgChildrenEntity1);
OrganizationEntity orgChildrenEntity2 = new OrganizationEntity();
orgChildrenEntity2.setId("3");
orgChildrenEntity2.setCode("1003");
orgChildrenEntity2.setName("成都分部");
orgChildrenEntity2.setType("默认");
Map<String, Object> childrenProperties2 = new HashMap<>();
childrenProperties2.put("province", "成都");
childrenProperties2.put("alias", "child-2");
orgChildrenEntity2.setProperties(childrenProperties2);
orgChildrenEntity2.setParentId("1");
orgChildrenEntity2.setPath("1001-1003");
orgChildrenEntity2.setLevel(2);
orgChildrenEntity2.setSortIndex(3L);
orgList.add(orgChildrenEntity2);
OrganizationEntity orgChildrenEntity3 = new OrganizationEntity();
orgChildrenEntity3.setId("4");
orgChildrenEntity3.setCode("1004");
orgChildrenEntity3.setName("沙坪坝办事点");
orgChildrenEntity3.setType("默认");
Map<String, Object> childrenProperties3 = new HashMap<>();
childrenProperties3.put("province", "重庆");
childrenProperties3.put("alias", "child-3");
orgChildrenEntity3.setProperties(childrenProperties3);
orgChildrenEntity3.setParentId("2");
orgChildrenEntity3.setPath("1001-1002-1004");
orgChildrenEntity3.setLevel(3);
orgChildrenEntity3.setSortIndex(4L);
orgList.add(orgChildrenEntity3);
return orgList;
}
}

View File

@ -0,0 +1,81 @@
package org.jetlinks.community.auth.web;
import org.hswebframework.web.authorization.DefaultDimensionType;
import org.hswebframework.web.authorization.ReactiveAuthenticationInitializeService;
import org.hswebframework.web.system.authorization.api.entity.UserEntity;
import org.hswebframework.web.system.authorization.api.service.reactive.ReactiveUserService;
import org.jetlinks.community.auth.entity.RoleEntity;
import org.jetlinks.community.test.spring.TestJetLinksController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.http.MediaType;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.Collections;
@WebFluxTest(RoleController.class)
class RoleControllerTest extends TestJetLinksController {
@Autowired
private ReactiveAuthenticationInitializeService initializeService;
@Autowired
private ReactiveUserService userService;
@Test
void testBind() {
RoleEntity role = new RoleEntity();
role.setId("test");
role.setName("Test");
UserEntity user = new UserEntity();
user.setName("Test");
user.setUsername("test");
user.setPassword("Test123456");
userService.saveUser(Mono.just(user))
.block();
client
.post()
.uri("/role")
.bodyValue(role)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/role/{id}/users/_bind", role.getId())
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Collections.singletonList(user.getId()))
.exchange()
.expectStatus()
.is2xxSuccessful();
initializeService
.initUserAuthorization(user.getId())
.as(StepVerifier::create)
.expectNextMatches(auth -> auth.hasDimension(DefaultDimensionType.role, role.getId()))
.verifyComplete();
client
.post()
.uri("/role/{id}/users/_unbind", role.getId())
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Collections.singletonList(user.getId()))
.exchange()
.expectStatus()
.is2xxSuccessful();
initializeService
.initUserAuthorization(user.getId())
.as(StepVerifier::create)
.expectNextMatches(auth -> !auth.hasDimension(DefaultDimensionType.role, role.getId()))
.verifyComplete();
}
}

View File

@ -0,0 +1,69 @@
package org.jetlinks.community.auth.web;
import org.jetlinks.community.auth.entity.ThirdPartyUserBindEntity;
import org.jetlinks.community.auth.web.request.ThirdPartyBindUserInfo;
import org.jetlinks.community.test.spring.TestJetLinksController;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.http.MediaType;
@WebFluxTest(ThirdPartyUserController.class)
class ThirdPartyUserControllerTest extends TestJetLinksController {
@Test
void test() {
String type = "wx-corp";
String provider = "corp1";
client.patch()
.uri("/user/third-party/{type}/{provider}", type, provider)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(ThirdPartyBindUserInfo.of(
"test","test", "用户1", "u1"
))
.exchange()
.expectStatus()
.is2xxSuccessful();
client.get()
.uri("/user/third-party/{type}/{provider}", type, provider)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.jsonPath("$[0].userId").isEqualTo("test")
.jsonPath("$[0].providerName").isEqualTo("用户1")
.jsonPath("$[0].thirdPartyUserId").isEqualTo("u1");
client.get()
.uri("/user/third-party/me")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.jsonPath("$[0].userId").isEqualTo("test")
.jsonPath("$[0].providerName").isEqualTo("用户1")
.jsonPath("$[0].thirdPartyUserId").isEqualTo("u1")
;
client.delete()
.uri("/user/third-party/me/{id}", ThirdPartyUserBindEntity.generateId(
type, provider, "u1"
))
.exchange()
.expectStatus()
.is2xxSuccessful();
client.get()
.uri("/user/third-party/me")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBodyList(ThirdPartyUserBindEntity.class)
.hasSize(0)
;
}
}

View File

@ -0,0 +1,121 @@
package org.jetlinks.community.auth.web;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.simple.SimpleUser;
import org.hswebframework.web.system.authorization.api.entity.UserEntity;
import org.jetlinks.community.auth.entity.UserDetail;
import org.jetlinks.community.auth.service.request.SaveUserDetailRequest;
import org.jetlinks.community.test.spring.TestJetLinksController;
import org.jetlinks.community.test.web.TestAuthentication;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import reactor.test.StepVerifier;
import java.util.HashMap;
import java.util.Map;
/**
* 用户信息接口.
*
* @author zhangji
* @version 1.11 2021/9/29
*/
@DisplayName("用户信息接口UserDetailController")
@WebFluxTest({UserDetailController.class, WebFluxUserController.class})
class UserDetailControllerTest extends TestJetLinksController {
public static final String BASE_URI = "/user/detail";
private String userId;
@Override
protected void initAuth(TestAuthentication authentication) {
super.initAuth(authentication);
// 初始化用户ID
UserEntity userEntity = new UserEntity();
userEntity.setUsername(authentication.getUser().getUsername());
userEntity.generateId();
userId = userEntity.getId();
// 修改当前用户的ID和数据库保持一致
((SimpleUser) authentication.getUser()).setId(userEntity.getId());
}
/**
* 保存用户
*/
@BeforeEach
void addUser() {
Map<String, String> userMap = new HashMap<>();
userMap.put("name", "test");
userMap.put("username", "test");
userMap.put("password", "password123456");
userMap.put("type", "test");
Boolean result = client.patch()
.uri("/user")
.bodyValue(userMap)
.exchange()
.expectBody(Boolean.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(result);
Assertions.assertTrue(result);
}
@Test
void test() {
// 保存当前用户详情
SaveUserDetailRequest saveRequest = new SaveUserDetailRequest();
saveRequest.setName("测试用户");
saveRequest.setDescription("单元测试-保存");
saveRequest.setEmail("test@email.com");
saveRequest.setTelephone("023-88888888");
client.put()
.uri(BASE_URI)
.bodyValue(saveRequest)
.exchange()
.expectStatus()
.is2xxSuccessful();
// 获取当前登录用户详情
client.get()
.uri(BASE_URI)
.exchange()
.expectBody()
.jsonPath("$.name").isEqualTo(saveRequest.getName())
.jsonPath("$.email").isEqualTo(saveRequest.getEmail())
.jsonPath("$.telephone").isEqualTo(saveRequest.getTelephone())
.jsonPath("$.description").isEqualTo(saveRequest.getDescription());
// 删除用户
Boolean deleteResult = client.delete()
.uri("/user/{id:.+}", userId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody(Boolean.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(deleteResult);
Assertions.assertTrue(deleteResult);
// 获取当前登录用户详情用户数据不存在返回默认数据
UserDetail userDetail = client.get()
.uri(BASE_URI)
.exchange()
.expectBody(UserDetail.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(userDetail);
Authentication
.currentReactive()
.map(i -> i.getUser().getName().equals(userDetail.getName()))
.as(StepVerifier::create)
.expectNext(true)
.verifyComplete();
}
}

View File

@ -0,0 +1,82 @@
package org.jetlinks.community.auth.web;
import org.jetlinks.community.auth.entity.UserSettingEntity;
import org.jetlinks.community.test.spring.TestJetLinksController;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.http.MediaType;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@WebFluxTest(UserSettingController.class)
class UserSettingControllerTest extends TestJetLinksController {
@Test
void testCrud() {
String type = "user-search";
UserSettingEntity newSetting = new UserSettingEntity();
newSetting.setName("test");
newSetting.setContent("test-content");
client
.post()
.uri("/user/settings/{type}", type)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(newSetting)
.exchange()
.expectStatus()
.is2xxSuccessful();
List<UserSettingEntity> entities = client
.get()
.uri("/user/settings/{type}", type)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBodyList(UserSettingEntity.class)
.returnResult()
.getResponseBody();
assertNotNull(entities);
assertEquals(1, entities.size());
UserSettingEntity entity = client
.get()
.uri("/user/settings/{type}/{key}", type, entities.get(0).getKey())
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody(UserSettingEntity.class)
.returnResult()
.getResponseBody();
assertNotNull(entity);
assertEquals(entity.getContent(), newSetting.getContent());
client
.delete()
.uri("/user/settings/{type}/{key}", type, entity.getKey())
.exchange()
.expectStatus()
.is2xxSuccessful();
UserSettingEntity entity1 = client
.get()
.uri("/user/settings/{type}/{key}", type, entity.getKey())
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody(UserSettingEntity.class)
.returnResult()
.getResponseBody();
assertNull(entity1);
}
}

View File

@ -0,0 +1,225 @@
package org.jetlinks.community.auth.web;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.authorization.simple.SimpleUser;
import org.hswebframework.web.system.authorization.api.entity.UserEntity;
import org.jetlinks.community.test.spring.TestJetLinksController;
import org.jetlinks.community.test.web.TestAuthentication;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 输入描述.
*
* @author zhangji
* @version 1.11 2021/9/29
*/
@DisplayName("用户管理WebFluxUserController")
@WebFluxTest(WebFluxUserController.class)
public class WebFluxUserControllerTest extends TestJetLinksController {
public static final String BASE_URI = "/user";
public String userId;
public static final String TENANT_ID = "tenant-test";
@Override
protected void initAuth(TestAuthentication authentication) {
super.initAuth(authentication);
// 添加租户
// authentication.addDimension(TenantDimensionType.tenant.getId(), TENANT_ID);
// 初始化用户ID
UserEntity userEntity = new UserEntity();
userEntity.setUsername(authentication.getUser().getUsername());
userEntity.generateId();
userId = userEntity.getId();
// 修改当前用户的ID和数据库保持一致
((SimpleUser) authentication.getUser()).setId(userEntity.getId());
}
/**
* 保存用户
*/
@BeforeEach
void addUser() {
Map<String, String> userMap = new HashMap<>();
userMap.put("name", "test");
userMap.put("username", "test");
userMap.put("password", "password123456");
userMap.put("type", "test");
Boolean result = client.patch()
.uri("/user")
.bodyValue(userMap)
.exchange()
.expectBody(Boolean.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(result);
Assertions.assertTrue(result);
}
/**
* 删除用户
*/
@AfterEach
void deleteUser() {
Boolean deleteResult = client.delete()
.uri("/user/{id:.+}", userId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody(Boolean.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(deleteResult);
Assertions.assertTrue(deleteResult);
}
@Test
void testUsernameValidate() {
client.post()
.uri("/user/username/_validate")
.bodyValue("test")
.exchange()
.expectBody()
.jsonPath("$.passed")
.isEqualTo(false);
}
@Test
void testPasswordValidate() {
client.post()
.uri("/user/password/_validate")
.bodyValue("1111")
.exchange()
.expectBody()
.jsonPath("$.passed")
.isEqualTo(false);
}
@Test
void testPasswordReset() {
client.post()
.uri("/user/" + userId + "/password/_reset")
.bodyValue("pwd1qaz2wsx")
.exchange()
.expectBody(Integer.class)
.isEqualTo(1);
}
@Test
void test() {
// 修改用户
Map<String, String> userMap = new HashMap<>();
userMap.put("id", userId);
userMap.put("name", "test-update");
Boolean result = client.patch()
.uri("/user")
.bodyValue(userMap)
.exchange()
.expectBody(Boolean.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(result);
Assertions.assertTrue(result);
// 使用POST方式分页动态查询(不返回总数)
List<UserEntity> userList = client.post()
.uri(BASE_URI + "/_query/no-paging")
.bodyValue(QueryParamEntity.of())
.exchange()
.expectBodyList(UserEntity.class)
.returnResult()
.getResponseBody();
Assertions.assertFalse(CollectionUtils.isEmpty(userList));
// 使用GET方式分页动态查询(不返回总数)
List<UserEntity> userList2 = client.get()
.uri(BASE_URI + "/_query/no-paging")
.exchange()
.expectBodyList(UserEntity.class)
.returnResult()
.getResponseBody();
Assertions.assertFalse(CollectionUtils.isEmpty(userList2));
// 使用POST方式分页动态查询
PagerResult<UserEntity> userPager = client.post()
.uri(BASE_URI + "/_query")
.bodyValue(QueryParamEntity.of())
.exchange()
.expectBody(new ParameterizedTypeReference<PagerResult<UserEntity>>() {
})
.returnResult()
.getResponseBody();
Assertions.assertNotNull(userPager);
Assertions.assertFalse(CollectionUtils.isEmpty(userPager.getData()));
// 使用GET方式分页动态查询
PagerResult<UserEntity> userPager2 = client.get()
.uri(BASE_URI + "/_query")
.exchange()
.expectBody(new ParameterizedTypeReference<PagerResult<UserEntity>>() {
})
.returnResult()
.getResponseBody();
Assertions.assertNotNull(userPager2);
Assertions.assertFalse(CollectionUtils.isEmpty(userPager2.getData()));
// 使用POST方式查询总数
Integer total = client.post()
.uri(BASE_URI + "/_count")
.bodyValue(QueryParamEntity.of())
.exchange()
.expectBody(Integer.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(total);
Assertions.assertTrue(total > 0);
// 使用GET方式查询总数
Integer total2 = client.get()
.uri(BASE_URI + "/_count")
.exchange()
.expectBody(Integer.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(total2);
Assertions.assertTrue(total2 > 0);
// 修改用户状态
Boolean stateResult = client.put()
.uri(BASE_URI + "/{id:.+}/{state}", userId, "0")
.exchange()
.expectBody(Boolean.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(stateResult);
Assertions.assertTrue(stateResult);
// 根据ID查询
UserEntity user = client.get()
.uri(BASE_URI + "/{id:.+}", userId)
.exchange()
.expectBody(UserEntity.class)
.returnResult()
.getResponseBody();
Assertions.assertNotNull(user);
Assertions.assertEquals(userId, user.getId());
}
}

View File

@ -99,6 +99,13 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetlinks.community</groupId>
<artifactId>test-component</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,73 @@
package org.jetlinks.community.device;
import org.hswebframework.web.authorization.token.DefaultUserTokenManager;
import org.hswebframework.web.authorization.token.UserTokenManager;
import org.hswebframework.web.starter.jackson.CustomCodecsAutoConfiguration;
import org.jetlinks.community.configure.cluster.ClusterConfiguration;
import org.jetlinks.community.configure.device.DeviceClusterConfiguration;
import org.jetlinks.community.elastic.search.configuration.ElasticSearchConfiguration;
import org.jetlinks.core.ProtocolSupports;
import org.jetlinks.core.device.DeviceRegistry;
import org.jetlinks.core.device.StandaloneDeviceMessageBroker;
import org.jetlinks.core.device.session.DeviceSessionManager;
import org.jetlinks.core.server.MessageHandler;
import org.jetlinks.supports.device.session.LocalDeviceSessionManager;
import org.jetlinks.supports.official.JetLinksDeviceMetadataCodec;
import org.jetlinks.supports.test.InMemoryDeviceRegistry;
import org.jetlinks.supports.test.MockProtocolSupport;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ImportAutoConfiguration({
CodecsAutoConfiguration.class,
JacksonAutoConfiguration.class,
CustomCodecsAutoConfiguration.class,
ClusterConfiguration.class,
DeviceClusterConfiguration.class
})
public class DeviceTestConfiguration {
@Bean
public ProtocolSupports mockProtocolSupport(){
return new MockProtocolSupport();
}
@Bean
public UserTokenManager userTokenManager(){
return new DefaultUserTokenManager();
}
@Bean
public DeviceRegistry deviceRegistry() {
return new InMemoryDeviceRegistry();
}
@Bean
public MessageHandler messageHandler() {
return new StandaloneDeviceMessageBroker();
}
@Bean
public DeviceSessionManager deviceSessionManager() {
return LocalDeviceSessionManager.create();
}
@Bean
public ElasticSearchConfiguration searchConfiguration() {
return new ElasticSearchConfiguration();
}
@Bean
public JetLinksDeviceMetadataCodec jetLinksDeviceMetadataCodec(){
return new JetLinksDeviceMetadataCodec();
}
}

View File

@ -0,0 +1,695 @@
package org.jetlinks.community.device.web;
import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import org.jetlinks.community.test.spring.TestJetLinksController;
import org.jetlinks.core.metadata.SimplePropertyMetadata;
import org.jetlinks.core.metadata.types.FloatType;
import org.jetlinks.community.PropertyMetadataConstants;
import org.jetlinks.community.PropertyMetric;
import org.jetlinks.community.device.entity.DeviceInstanceEntity;
import org.jetlinks.community.device.entity.DeviceProductEntity;
import org.jetlinks.community.device.entity.DeviceTagEntity;
import org.jetlinks.community.device.service.LocalDeviceInstanceService;
import org.jetlinks.community.device.service.LocalDeviceProductService;
import org.jetlinks.community.device.service.data.DeviceDataService;
import org.jetlinks.community.device.web.request.AggRequest;
import org.jetlinks.community.relation.entity.RelationEntity;
import org.jetlinks.community.relation.service.RelatedObjectInfo;
import org.jetlinks.community.relation.service.RelationService;
import org.jetlinks.community.relation.service.request.SaveRelationRequest;
import org.jetlinks.community.timeseries.query.Aggregation;
import org.jetlinks.supports.official.JetLinksDeviceMetadata;
import org.jetlinks.supports.official.JetLinksDeviceMetadataCodec;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import reactor.test.StepVerifier;
import java.util.*;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@WebFluxTest(value = DeviceInstanceController.class, properties = {
"spring.reactor.debug-agent.enabled=true"
})
class DeviceInstanceControllerTest extends TestJetLinksController {
@Autowired
@SuppressWarnings("all")
private LocalDeviceInstanceService deviceService;
@Autowired
@SuppressWarnings("all")
private LocalDeviceProductService productService;
private String deviceId;
private String productId;
private String metadata;
@BeforeEach
void setup() {
DeviceProductEntity product = new DeviceProductEntity();
product.setMetadata("{}");
product.setTransportProtocol("MQTT");
product.setMessageProtocol("test");
product.setId(productId = "deviceinstancecontrollertest_product");
product.setName("DeviceInstanceControllerTest");
JetLinksDeviceMetadata metadata = new JetLinksDeviceMetadata("Test", "Test");
{
SimplePropertyMetadata metric = SimplePropertyMetadata.of(
"metric", "Metric", FloatType.GLOBAL
);
metric.setExpands(
PropertyMetadataConstants.Metrics
.metricsToExpands(Arrays.asList(
PropertyMetric.of("max", "最大值", 100),
PropertyMetric.of("min", "最小值", -100)
))
);
metadata.addProperty(metric);
}
product.setMetadata(this.metadata=JetLinksDeviceMetadataCodec.getInstance().doEncode(metadata));
productService
.save(product)
.then(productService.deploy(productId))
.then()
.as(StepVerifier::create)
.expectComplete()
.verify();
DeviceInstanceEntity device = new DeviceInstanceEntity();
device.setId(deviceId = "deviceinstancecontrollertest_device");
device.setName("DeviceInstanceControllerTest");
device.setProductId(product.getId());
device.setProductName(device.getName());
client
.patch()
.uri("/device/instance")
.bodyValue(device)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.put()
.uri("/device/instance/batch/_deploy")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Arrays.asList(deviceId))
.exchange()
.expectStatus()
.is2xxSuccessful();
}
@AfterEach
void shutdown() {
client
.put()
.uri("/device/instance/batch/_unDeploy")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Arrays.asList(deviceId))
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.put()
.uri("/device/instance/batch/_delete")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Arrays.asList(deviceId))
.exchange()
.expectStatus()
.is2xxSuccessful();
}
@Test
@SneakyThrows
void testCommon() {
// {
// DeviceInstanceEntity device = new DeviceInstanceEntity();
// device.setId(deviceId);
// device.setName("DeviceInstanceControllerTest");
// device.setProductId(productId);
// device.setProductName(device.getName());
// //重复创建
// client
// .post()
// .uri("/device/instance")
// .bodyValue(device)
// .exchange()
// .expectStatus()
// .is4xxClientError();
// }
client
.get()
.uri("/device/instance/{id:.+}/detail", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.get()
.uri("/device/instance/bind-providers")
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.get()
.uri("/device/instance/{deviceId}/state", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.get()
.uri("/device/instance/state/_sync")
.accept(MediaType.TEXT_EVENT_STREAM)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/device/instance/{deviceId}/deploy", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/device/instance/{deviceId}/undeploy", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/device/instance/{deviceId}/disconnect", deviceId)
.exchange();
}
@Test
void testProperties() {
client
.get()
.uri("/device/instance/{deviceId:.+}/properties/_query?where=property is test", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.get()
.uri("/device/instance/{deviceId:.+}/properties/_query", deviceId)
.exchange()
.expectStatus()
.is4xxClientError();
client
.get()
.uri("/device/instance/{deviceId:.+}/properties/latest", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.get()
.uri("/device/instance/{deviceId:.+}/properties", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/device/instance/{deviceId:.+}/properties/_top/{numberOfTop}", deviceId, 1)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("{}")
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.get()
.uri("/device/instance/{deviceId:.+}/property/{property}/_query", deviceId, "test")
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/device/instance/{deviceId:.+}/property/{property}/_query", deviceId, "test")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("{}")
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/device/instance/{deviceId:.+}/properties/_query", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("{}")
.exchange();
client
.get()
.uri("/device/instance/{deviceId:.+}/property/{property:.+}", deviceId, "test")
.exchange()
.expectStatus()
.is2xxSuccessful();
AggRequest request = new AggRequest();
request.setColumns(Arrays.asList(
new DeviceDataService.DevicePropertyAggregation("test", "alias", Aggregation.AVG)
));
request.setQuery(DeviceDataService.AggregationRequest
.builder()
.build());
client
.post()
.uri("/device/instance/{deviceId:.+}/agg/_query", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(request)
.exchange();
}
@Test
void testEvent() {
client
.get()
.uri("/device/instance/{deviceId:.+}/event/{eventId}", deviceId, "test")
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/device/instance/{deviceId:.+}/event/{eventId}", deviceId, "test")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("{}")
.exchange()
.expectStatus()
.is2xxSuccessful();
}
@Test
void testLog() {
client
.get()
.uri("/device/instance/{deviceId:.+}/logs", deviceId, "test")
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/device/instance/{deviceId:.+}/logs", deviceId, "test")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("{}")
.exchange()
.expectStatus()
.is2xxSuccessful();
}
@Test
void testTag() {
DeviceTagEntity tag = new DeviceTagEntity();
tag.setKey("test");
tag.setValue("value");
client
.patch()
.uri("/device/instance/{deviceId}/tag", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(tag)
.exchange()
.expectStatus()
.is2xxSuccessful();
List<DeviceTagEntity> tags = client
.get()
.uri("/device/instance/{deviceId}/tags", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBodyList(DeviceTagEntity.class)
.returnResult()
.getResponseBody();
assertNotNull(tags);
assertFalse(tags.isEmpty());
client
.delete()
.uri("/device/instance/{deviceId}/tag/{tagId}", deviceId, tags.get(0).getId())
.exchange()
.expectStatus()
.is2xxSuccessful();
}
@Test
@SneakyThrows
void testMetadata() {
client
.get()
.uri("/device/instance/{id:.+}/config-metadata", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.get()
.uri("/device/instance/{id:.+}/config-metadata/{metadataType}/{metadataId}/{typeId}",
deviceId,
"property",
"temp",
"test")
.exchange()
.expectStatus()
.is2xxSuccessful();
String metadata = client
.post()
.uri("/device/instance/{deviceId}/property-metadata/import?fileUrl=" + new ClassPathResource("property-metadata.csv")
.getFile()
.getAbsolutePath(), deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody(String.class)
.returnResult()
.getResponseBody();
assertNotNull(metadata);
client
.put()
.uri("/device/instance/{id:.+}/metadata", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(metadata)
.exchange()
.expectStatus()
.is2xxSuccessful();
deviceService.findById(deviceId)
.as(StepVerifier::create)
.expectNextMatches(device -> Objects.equals(
device.getDeriveMetadata(),
metadata
))
.expectComplete()
.verify();
client
.put()
.uri("/device/instance/{id}/metadata/merge-product", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.delete()
.uri("/device/instance/{id}/metadata", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
deviceService
.findById(deviceId)
.as(StepVerifier::create)
.expectNextMatches(device -> StringUtils.isEmpty(device.getDeriveMetadata()))
.expectComplete()
.verify();
}
@Test
void testConfiguration() {
deviceService.deploy(deviceId)
.then()
.as(StepVerifier::create)
.expectComplete()
.verify();
client
.post()
.uri("/device/instance/{deviceId:.+}/configuration/_write", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("{\"test\":\"123\"}")
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.post()
.uri("/device/instance/{deviceId:.+}/configuration/_read", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("[\"test\"]")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody(Map.class)
.isEqualTo(Collections.singletonMap("test", "123"));
client
.put()
.uri("/device/instance/{deviceId:.+}/configuration/_reset", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.put()
.uri("/device/instance/{deviceId:.+}/shadow", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("{\"test\":\"123\"}")
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.get()
.uri("/device/instance/{deviceId:.+}/shadow", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody(String.class)
.isEqualTo("{\"test\":\"123\"}");
}
@Test
void testCommand() {
client
.put()
.uri("/device/instance/{deviceId:.+}/property", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Collections.singletonMap("test", "value"))
.exchange();
client
.post()
.uri("/device/instance/{deviceId:.+}/property/_read", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Collections.singleton("test"))
.exchange();
client
.post()
.uri("/device/instance/{deviceId:.+}/function/test", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Collections.singletonMap("test", "value"))
.exchange();
client
.post()
.uri("/device/instance/{deviceId:.+}/message", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Collections.singletonMap("properties", Collections.singletonList("test")))
.exchange()
.expectStatus()
.is2xxSuccessful();
Map<String, Object> data = new HashMap<>();
data.put("deviceId", "test");
data.put("properties", Collections.singletonList("test"));
client
.post()
.uri("/device/instance/messages", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(data)
.exchange()
.expectStatus()
.is2xxSuccessful();
}
@Test
void testAutoChangeProductInfo() {
client.post()
.uri("/device/instance")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("{\"id\":\"testAutoChangeProductInfo\",\"name\":\"Test\",\"productId\":\"" + productId + "\"}")
.exchange()
.expectStatus()
.is2xxSuccessful();
}
@Autowired
private RelationService relationService;
@Test
void testRelation() {
RelationEntity entity = new RelationEntity();
entity.setRelation("manager");
entity.setObjectType("device");
entity.setObjectTypeName("设备");
entity.setTargetType("user");
entity.setTargetTypeName("用户");
entity.setName("管理员");
relationService
.save(entity).block();
client.get()
.uri("/device/instance/{deviceId}/detail", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.jsonPath("$.relations[0].relation").isEqualTo("manager")
.jsonPath("$.relations[0].related").isEmpty();
client.patch()
.uri("/device/instance/{deviceId}/relations", deviceId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(SaveRelationRequest.of(
"user",
"manager",
Arrays.asList(RelatedObjectInfo.of("admin", "管理员")),
null
))
.exchange()
.expectStatus()
.is2xxSuccessful();
client.get()
.uri("/device/instance/{deviceId}/detail", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.jsonPath("$.relations[0].relation").isEqualTo("manager")
.jsonPath("$.relations[0].related[0].id").isEqualTo("admin")
.jsonPath("$.relations[0].related[0].name").isEqualTo("管理员");
}
@Test
void testMetric() {
client.get()
.uri("/device/instance/{deviceId}/metric/property/{property}", deviceId, "metric")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.jsonPath("[0].id").isEqualTo("max")
.jsonPath("[0].value").isEqualTo(100)
.jsonPath("[1].id").isEqualTo("min")
.jsonPath("[1].value").isEqualTo(-100);
client.patch()
.uri("/device/instance/{deviceId}/metric/property/{property}", deviceId, "metric")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(PropertyMetric.of("max", "最大值", 110))
.exchange()
.expectStatus()
.is2xxSuccessful();
client.get()
.uri("/device/instance/{deviceId}/metric/property/{property}", deviceId, "metric")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.jsonPath("[0].id").isEqualTo("max")
.jsonPath("[0].value").isEqualTo(110)
.jsonPath("[1].id").isEqualTo("min")
.jsonPath("[1].value").isEqualTo(-100);
}
@Test
void testDetail(){
client
.get()
.uri("/device/instance/{deviceId}/detail", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.jsonPath("$.metadata").isEqualTo(metadata);
String newMetadata="{\"properties\":[]}";
client
.put()
.uri("/device/product/{productId}", productId)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("{\"metadata\":"+ JSON.toJSONString(newMetadata)+"}")
.exchange()
.expectStatus()
.is2xxSuccessful();
//仅保存 未发布
client
.get()
.uri("/device/instance/{deviceId}/detail", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.jsonPath("$.metadata").isEqualTo(metadata);
client
.post()
.uri("/device/product/{productId}/deploy", productId)
.exchange()
.expectStatus()
.is2xxSuccessful();
client
.get()
.uri("/device/instance/{deviceId}/detail", deviceId)
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.jsonPath("$.metadata").isEqualTo(newMetadata);
}
}

View File

@ -11,5 +11,7 @@ COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/application/ ./
COPY docker-entrypoint.sh ./
COPY entrypoint.sh ./
RUN chmod +x docker-entrypoint.sh
RUN chmod +x entrypoint.sh
ENTRYPOINT ["./docker-entrypoint.sh"]