Browse Source

Merge remote-tracking branch 'origin/lts-4.2' into lts-4.3

pull/15007/head
Viacheslav Klimov 8 months ago
parent
commit
ce92a0af96
  1. 1
      .gitignore
  2. 39
      TEST_FAST.md
  3. 10
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java
  4. 52
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
  5. 60
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java
  6. 31
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java

1
.gitignore

@ -37,3 +37,4 @@ rebuild-docker.sh
*/.run/** */.run/**
.run/** .run/**
.run .run
.claude/

39
TEST_FAST.md

@ -10,22 +10,43 @@ mvn clean install -T6 -DskipTests
mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4 mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4
mvn test -pl dao -Dparallel=packages -DforkCount=4 mvn test -pl dao -Dparallel=packages -DforkCount=4
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
#the rest of application tests #the rest of application tests
mvn test -pl application -Dtest=' mvn test -pl application -Dtest='
!**/nosql/*Test.java, !**/nosql/**,
!org.thingsboard.server.controller.**, !org.thingsboard.server.controller.**,
!org.thingsboard.server.edge.**, !org.thingsboard.server.edge.**,
!org.thingsboard.server.service.**, !org.thingsboard.server.service.**,
!org.thingsboard.server.transport.mqtt.**, !org.thingsboard.server.transport.mqtt.**,
!org.thingsboard.server.transport.coap.**, !org.thingsboard.server.transport.coap.**,
!org.thingsboard.server.transport.lwm2m.** !org.thingsboard.server.transport.lwm2m.**,
!**/*TestSuite.java
' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 ' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
``` ```
## Testcontainers compatibility with the Docker API workaround
In case your tests failed to run testcontainers due to unsupported Docker API version
:coffee: testcontainers (Docker API 1.32) + :whale: docker 29 (min API 1.44) workaround
Add to /etc/docker/daemon.json and restart docker
```json
{
"min-api-version": "1.32"
}
```
Same works on Mac, except `daemon.json` are located in another folder and required to be edited from Docker Desktop UI.
Tip: If your testcontainer are struggling to find any Docker. You can try to remove the testcontainers property file. It will be recreated on the next testcontainers run.
```bash
rm ~/.testcontainers.properties
```

10
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java

@ -29,18 +29,10 @@ public class RestAuthenticationDetails implements Serializable {
private final Client userAgent; private final Client userAgent;
public RestAuthenticationDetails(HttpServletRequest request) { public RestAuthenticationDetails(HttpServletRequest request) {
this.clientAddress = getClientIP(request); this.clientAddress = request.getRemoteAddr();
this.userAgent = getUserAgent(request); this.userAgent = getUserAgent(request);
} }
private static String getClientIP(HttpServletRequest request) {
String xfHeader = request.getHeader("X-Forwarded-For");
if (xfHeader == null) {
return request.getRemoteAddr();
}
return xfHeader.split(",")[0];
}
private static Client getUserAgent(HttpServletRequest request) { private static Client getUserAgent(HttpServletRequest request) {
Parser uaParser = new Parser(); Parser uaParser = new Parser();
return uaParser.parse(request.getHeader("User-Agent")); return uaParser.parse(request.getHeader("User-Agent"));

52
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java

@ -140,45 +140,61 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
} }
private void startDownloading() { private void startDownloading() {
long delay = 0;
// Step 1: state = 1
scheduler.schedule(() -> { scheduler.schedule(() -> {
try { state.set(1);
state.set(1); fireResourceChange(3);
fireResourceChange(3); log.info("Downloading started: state=[{}]", state.get());
Thread.sleep(100); }, delay, TimeUnit.MILLISECONDS);
state.set(2);
fireResourceChange(3); delay += 100; // next step after 100 ms
} catch (Exception e) {
} // Step 2: state = 2
}, 100, TimeUnit.MILLISECONDS); scheduler.schedule(() -> {
state.set(2);
fireResourceChange(3);
log.info("Downloading in progress: state=[{}]", state.get());
}, delay, TimeUnit.MILLISECONDS);
} }
private void startUpdating(LwM2mServer identity) { private void startUpdating(LwM2mServer identity) {
scheduler.schedule(() -> { scheduler.schedule(() -> {
try { try {
// Update state + result
state.set(3); state.set(3);
fireResourceChange(3); fireResourceChange(3);
Thread.sleep(100);
updateResult.set(1); updateResult.set(1);
fireResourceChange(5); fireResourceChange(5);
this.pkgName = TITLE;
fireResourceChange(6);
this.pkgVersion = TARGET_FW_VERSION;
fireResourceChange(7);
if (this.leshanClient != null) { if (this.leshanClient != null) {
log.info("Stop/reboot LwM2M client {}", this.leshanClient.getEndpoint(identity)); log.info("Stop/reboot LwM2M client {}", this.leshanClient.getEndpoint(identity));
this.leshanClient.stop(false); this.leshanClient.stop(false);
log.info("Start after update fw LwM2M client {}", this.leshanClient.getEndpoint(identity)); log.info("Start after update fw LwM2M client {}", this.leshanClient.getEndpoint(identity));
this.leshanClient.start(); this.leshanClient.start();
this.pkgName = this.pkgNameDef;
this.pkgVersion = this.pkgVersionDef; // Delayed reset pkgName/pkgVersion, after reboot + registration
scheduler.schedule(() -> {
this.pkgName = this.pkgNameDef;
fireResourceChange(6);
this.pkgVersion = this.pkgVersionDef;
fireResourceChange(7);
log.info("FW resources updating to new values: pkgName=[{}], pkgVersion=[{}]",
this.pkgName, this.pkgVersion);
}, 15, TimeUnit.SECONDS); // 15 sec — safe timing
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Error during firmware update", e);
} }
}, 100, TimeUnit.MILLISECONDS); }, 0, TimeUnit.SECONDS); // start immediately, without further delay
} }
protected void setLeshanClient(LeshanClient leshanClient) { protected void setLeshanClient(LeshanClient leshanClient) {
this.leshanClient = leshanClient; this.leshanClient = leshanClient;
} }
} }

60
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java

@ -33,6 +33,8 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.controller.AbstractWebTest.TIMEOUT;
@Slf4j @Slf4j
public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
@ -85,10 +87,7 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {
case 2: case 2, 3:
startDownloading();
return WriteResponse.success();
case 3:
startDownloading(); startDownloading();
return WriteResponse.success(); return WriteResponse.success();
default: default:
@ -123,25 +122,34 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
} }
private void startDownloading() { private void startDownloading() {
long delay = 0;
// Step 1: start downloading
scheduler.schedule(() -> { scheduler.schedule(() -> {
try { state.set(1);
state.set(1); updateResult.set(1);
updateResult.set(1); fireResourceChange(7);
fireResourceChange(7); fireResourceChange(9);
fireResourceChange(9); }, delay, TimeUnit.MILLISECONDS);
Thread.sleep(100);
state.set(2); delay += 100;
fireResourceChange(7);
Thread.sleep(100); // Step 2: downloading in progress
state.set(3); scheduler.schedule(() -> {
fireResourceChange(7); state.set(2);
Thread.sleep(100); fireResourceChange(7);
updateResult.set(3); }, delay, TimeUnit.MILLISECONDS);
fireResourceChange(9);
} catch (Exception e) { delay += 100;
} // Step 3: downloading finished
}, 100, TimeUnit.MILLISECONDS); scheduler.schedule(() -> {
state.set(3);
fireResourceChange(7);
updateResult.set(3);
fireResourceChange(9);
}, delay, TimeUnit.MILLISECONDS);
} }
private void startUpdating() { private void startUpdating() {
@ -150,7 +158,13 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
updateResult.set(2); updateResult.set(2);
fireResourceChange(7); fireResourceChange(7);
fireResourceChange(9); fireResourceChange(9);
// Optional: delayed log about FW update
scheduler.schedule(() -> {
log.info("FW resources updating to new values: state=[{}], updateResult=[{}]",
state.get(), updateResult.get());
}, 500, TimeUnit.MILLISECONDS);
}, 100, TimeUnit.MILLISECONDS); }, 100, TimeUnit.MILLISECONDS);
} }
} }

31
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java

@ -56,6 +56,7 @@ import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@ -115,18 +116,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
assertTrue(request.containsHeader("Foo"), "Custom header included"); assertTrue(request.containsHeader("Foo"), "Custom header included");
assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value"); assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value");
response.setStatusCode(200); response.setStatusCode(200);
new Thread(new Runnable() { latch.countDown();
@Override
public void run() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
// ignore
} finally {
latch.countDown();
}
}
}).start();
} catch (Exception e) { } catch (Exception e) {
System.out.println("Exception handling request: " + e.toString()); System.out.println("Exception handling request: " + e.toString());
e.printStackTrace(); e.printStackTrace();
@ -158,7 +148,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
verify(ctx).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); verify(ctx, timeout(10_000)).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
assertNotSame(metaData, metadataCaptor.getValue()); assertNotSame(metaData, metadataCaptor.getValue());
assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue()); assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue());
@ -184,18 +174,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
assertTrue(request.containsHeader("Foo"), "Custom header included"); assertTrue(request.containsHeader("Foo"), "Custom header included");
assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value"); assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value");
response.setStatusCode(200); response.setStatusCode(200);
new Thread(new Runnable() { latch.countDown();
@Override
public void run() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
// ignore
} finally {
latch.countDown();
}
}
}).start();
} catch (Exception e) { } catch (Exception e) {
System.out.println("Exception handling request: " + e.toString()); System.out.println("Exception handling request: " + e.toString());
e.printStackTrace(); e.printStackTrace();
@ -227,7 +206,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
verify(ctx).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); verify(ctx, timeout(10_000)).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
assertNotSame(metaData, metadataCaptor.getValue()); assertNotSame(metaData, metadataCaptor.getValue());
assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue()); assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue());

Loading…
Cancel
Save