Skip to content

chore: separate unit/integration tests, run on PR #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/unittests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Run Unit Tests

on:
pull_request:
branches: [ main ]
push:
branches: [ main ]

jobs:
test:
name: Unit Tests
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Configure Maven for GitHub Packages
run: |
mkdir -p ~/.m2
cat > ~/.m2/settings.xml <<EOF
<settings>
<servers>
<server>
<id>github</id>
<username>${{ github.actor }}</username>
<password>${{ secrets.GITHUB_TOKEN }}</password>
</server>
</servers>
</settings>
EOF
- name: Run Unit Tests
run: mvn clean test
55 changes: 54 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.bigboxer23</groupId>
<artifactId>switchbotapi-java</artifactId>
<version>1.2.1</version>
<version>1.2.2</version>

<name>switchbotapi-java</name>
<url>https://github.com/bigboxer23/switchbotapi-java</url>
Expand All @@ -22,6 +22,12 @@
</properties>

<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.17.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
Expand Down Expand Up @@ -78,9 +84,56 @@
<release>17</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.3</version>
<configuration>
<includes>
<include>**/*IT.java</include>
<include>**/*ITCase.java</include>
<include>**/*IntegrationTest.java</include>
</includes>
<parallel>methods</parallel>
<threadCount>4</threadCount>
<useUnlimitedThreads>false</useUnlimitedThreads>
<skipITs>${!integration}</skipITs>
<rerunFailingTestsCount>1</rerunFailingTestsCount>
</configuration>
<executions>
<execution>
<id>integration-tests</id>
<goals>
<goal>integration-test</goal>
</goals>
<phase>integration-test</phase>
</execution>
<execution>
<id>verify</id>
<goals>
<goal>verify</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.3</version>
<configuration>
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/*IT.java</exclude>
<exclude>**/*ITCase.java</exclude>
<exclude>**/*IntegrationTest.java</exclude>
</excludes>
<parallel>methods</parallel>
<threadCount>4</threadCount>
<useUnlimitedThreads>false</useUnlimitedThreads>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ public class SwitchBotDeviceApi {

private Map<String, String> deviceIdToNames;

private long deviceIdToNamesCacheTime = -1;
protected long deviceIdToNamesCacheTime = -1;

protected SwitchBotDeviceApi(SwitchBotApi provider) {
this.provider = provider;
refreshDeviceNameMap();
}

public String getDeviceNameFromId(String deviceId) {
Expand Down
58 changes: 58 additions & 0 deletions src/test/java/com/bigboxer23/switch_bot/SwitchBotApiUnitTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.bigboxer23.switch_bot;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import com.bigboxer23.switch_bot.data.Device;
import com.bigboxer23.switch_bot.data.DeviceCommand;
import com.bigboxer23.utils.time.ITimeConstants;
import java.io.IOException;
import java.util.List;
import org.junit.jupiter.api.Test;

public class SwitchBotApiUnitTest {
@Test
public void testSingletonBehavior() {
SwitchBotApi mockApi1 = SwitchBotApi.getInstance("token", "secret");
SwitchBotApi mockApi2 = SwitchBotApi.getInstance("token", "secret");
assertSame(mockApi1, mockApi2, "Should return the same singleton instance");
}

@Test
public void testDeviceCommandSerialization() throws IOException {
SwitchBotApi api = SwitchBotApi.getInstance("token", "secret");

DeviceCommand originalCommand = new DeviceCommand("turnOn", "default");

String json = api.getMoshi().adapter(DeviceCommand.class).toJson(originalCommand);
assertNotNull(json);
assertTrue(json.contains("turnOn"));

DeviceCommand deserializedCommand =
api.getMoshi().adapter(DeviceCommand.class).fromJson(json);
assertNotNull(deserializedCommand);
assertEquals(originalCommand.getCommand(), deserializedCommand.getCommand());
assertEquals(originalCommand.getParameter(), deserializedCommand.getParameter());
assertEquals("command", deserializedCommand.getCommandType());
}

@Test
public void testDeviceNameCacheRefreshBehavior() throws IOException {
SwitchBotApi mockApi = mock(SwitchBotApi.class);
SwitchBotDeviceApi deviceApi = spy(new SwitchBotDeviceApi(mockApi));

deviceApi.deviceIdToNamesCacheTime = System.currentTimeMillis() - (ITimeConstants.HOUR * 2);

Device dummyDevice = new Device();
dummyDevice.setDeviceId("12345");
dummyDevice.setDeviceName("Test Device");
doReturn(List.of(dummyDevice)).when(deviceApi).getDevices();

String deviceName = deviceApi.getDeviceNameFromId("12345");
assertEquals("Test Device", deviceName);

reset(deviceApi);
deviceApi.getDeviceNameFromId("12345");
verify(deviceApi, times(0)).getDevices();
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
package com.bigboxer23.switch_bot;
package com.bigboxer23.switch_bot.integration;

import static org.junit.jupiter.api.Assertions.*;

import com.bigboxer23.switch_bot.IDeviceCommands;
import com.bigboxer23.switch_bot.IDeviceTypes;
import com.bigboxer23.switch_bot.SwitchBotApi;
import com.bigboxer23.switch_bot.data.Device;
import com.bigboxer23.utils.command.Command;
import com.bigboxer23.utils.properties.PropertyUtils;
import java.io.IOException;
import java.util.List;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

/** Need to define environment variables for SwitchBotToken/SwitchBotSecret to run tests */
public class SwitchBotApiTest {
@Tag("integration")
public class SwitchBotApiIntegrationTest {
private static final String token = PropertyUtils.getProperty("switchbot_token");

private static final String secret = PropertyUtils.getProperty("switchbot_secret");

private static final SwitchBotApi instance = SwitchBotApi.getInstance(token, secret);

@Test
public void testGetDevices() throws IOException {
List<Device> devices = instance.getDeviceApi().getDevices();
assertFalse(devices.isEmpty());
assertFalse(devices.isEmpty(), "Device list should not be empty");
assertNotNull(devices.get(0).getDeviceId());
}

Expand All @@ -37,9 +39,8 @@ public void getDeviceNameFromId() throws IOException {
public void testDeviceStatus() throws IOException {
try {
instance.getDeviceApi().getDeviceStatus("123");
fail();
} catch (IOException e) {

fail("Expected IOException for invalid device ID");
} catch (IOException ignored) {
}
for (Device device : instance.getDeviceApi().getDevices()) {
assertNotNull(device.getDeviceId());
Expand All @@ -55,17 +56,16 @@ public void testDeviceStatus() throws IOException {
}
case IDeviceTypes.CURTAIN -> {
assertTrue(status.getSlidePosition() >= 0);
assertTrue(status.isMoving());
assertTrue(status.getBattery() >= 0);
}
case IDeviceTypes.PLUG_MINI -> {
assertTrue("on".equals(status.getPower()) || "off".equals(status.getPower()));
assertTrue(status.getVoltage() > 0);
assertTrue(status.getVoltage() > -1);
assertTrue(status.getWatts() > -1);
assertTrue(status.getElectricityOfDay() >= 0);
assertTrue(status.getElectricCurrent() > -1);
}
case IDeviceTypes.WATER_DETECTOR -> assertTrue(status.isWet());
case IDeviceTypes.WATER_DETECTOR -> assertTrue(status.isWet() || status.isDry());
case IDeviceTypes.METER_PRO_CO2 -> assertTrue(status.getCo2() > 0);
}
}
Expand All @@ -80,25 +80,14 @@ public void testCurtainDeviceCommands() throws IOException, InterruptedException
.orElse(null);
assertNotNull(curtain);
assertNotNull(curtain.getDeviceId());
System.out.println("slide position "
+ instance.getDeviceApi().getDeviceStatus(curtain.getDeviceId()).getSlidePosition());

instance.getDeviceApi().sendDeviceControlCommands(curtain.getDeviceId(), IDeviceCommands.CURTAIN_CLOSE);
pollForStatus(() -> {
int slidePosition = instance.getDeviceApi()
.getDeviceStatus(curtain.getDeviceId())
.getSlidePosition();
System.out.println("slide position " + slidePosition);
return slidePosition >= 90;
});
pollForStatus(() ->
instance.getDeviceApi().getDeviceStatus(curtain.getDeviceId()).getSlidePosition() >= 90);

instance.getDeviceApi().sendDeviceControlCommands(curtain.getDeviceId(), IDeviceCommands.CURTAIN_OPEN);
pollForStatus(() -> {
int slidePosition = instance.getDeviceApi()
.getDeviceStatus(curtain.getDeviceId())
.getSlidePosition();
System.out.println("slide position " + slidePosition);
return slidePosition == 0;
});
pollForStatus(() ->
instance.getDeviceApi().getDeviceStatus(curtain.getDeviceId()).getSlidePosition() == 0);
}

@Test
Expand All @@ -120,8 +109,7 @@ public void testPlugDeviceCommands() throws IOException, InterruptedException {

private void pollForStatus(Command<Boolean> command) throws IOException, InterruptedException {
boolean result = command.execute();
for (int ai = 0; ai < 10 && !result; ai++) {
System.out.println("sleeping " + ai);
for (int i = 0; i < 10 && !result; i++) {
Thread.sleep(2000);
result = command.execute();
}
Expand Down