Skip to content
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
3 changes: 1 addition & 2 deletions src/main/java/com/bigboxer23/switch_bot/data/Device.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.bigboxer23.switch_bot.data;

import com.squareup.moshi.Json;
import lombok.Data;

import java.util.List;
import lombok.Data;

/** */
@Data
Expand Down
79 changes: 77 additions & 2 deletions src/test/java/com/bigboxer23/switch_bot/SwitchBotApiUnitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,96 @@
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.switch_bot.data.*;
import com.bigboxer23.utils.time.ITimeConstants;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import okhttp3.Response;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class SwitchBotApiUnitTest {

@BeforeEach
void resetSingleton() throws Exception {
java.lang.reflect.Field instanceField = SwitchBotApi.class.getDeclaredField("instance");
instanceField.setAccessible(true);
instanceField.set(null, null);
}

@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 testGetInstanceWithNullToken() {
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
SwitchBotApi.getInstance(null, "secret");
});
assertEquals("need to define token and secret values.", exception.getMessage());
}

@Test
public void testGetInstanceWithNullSecret() {
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
SwitchBotApi.getInstance("token", null);
});
assertEquals("need to define token and secret values.", exception.getMessage());
}

@Test
public void testAddAuthReturnsCallback() {
assertNotNull(SwitchBotApi.getInstance("testToken", "testSecret"));
}

@Test
public void testCheckForErrorWithSuccessfulResponse() {
SwitchBotApi api = SwitchBotApi.getInstance("token", "secret");
Response mockResponse = mock(Response.class);
ApiResponse successResponse = new ApiResponse();
successResponse.setStatusCode(100);
successResponse.setMessage("success");

IApiResponse result = api.checkForError(mockResponse, Optional.of(successResponse));

assertTrue(result.isSuccess());
assertEquals(100, result.getStatusCode());
}

@Test
public void testCheckForErrorWithFailedResponse() {
SwitchBotApi api = SwitchBotApi.getInstance("token", "secret");
Response mockResponse = mock(Response.class);
ApiResponse failedResponse = new ApiResponse();
failedResponse.setStatusCode(190);
failedResponse.setMessage("Device not found");

IApiResponse result = api.checkForError(mockResponse, Optional.of(failedResponse));

assertFalse(result.isSuccess());
assertEquals(190, result.getStatusCode());
assertEquals("Device not found", result.getMessage());
}

@Test
public void testCheckForErrorWithEmptyResponse() {
SwitchBotApi api = SwitchBotApi.getInstance("token", "secret");
Response mockResponse = mock(Response.class);
when(mockResponse.code()).thenReturn(404);
when(mockResponse.message()).thenReturn("Not Found");

IApiResponse result = api.checkForError(mockResponse, Optional.empty());

assertFalse(result.isSuccess());
assertEquals(404, result.getStatusCode());
assertEquals("Not Found", result.getMessage());
assertInstanceOf(BadApiResponse.class, result);
}

@Test
public void testDeviceCommandSerialization() throws IOException {
SwitchBotApi api = SwitchBotApi.getInstance("token", "secret");
Expand Down
173 changes: 173 additions & 0 deletions src/test/java/com/bigboxer23/switch_bot/SwitchBotDeviceApiTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package com.bigboxer23.switch_bot;

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

import com.bigboxer23.switch_bot.data.*;
import com.bigboxer23.utils.time.ITimeConstants;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class SwitchBotDeviceApiTest {

@Mock
private SwitchBotApi mockSwitchBotApi;

private SwitchBotDeviceApi deviceApi;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
deviceApi = new SwitchBotDeviceApi(mockSwitchBotApi);
}

@Test
public void testGetDeviceNameFromIdWithFreshCache() throws IOException {
Device device1 = new Device();
device1.setDeviceId("device1");
device1.setDeviceName("Living Room Light");

Device device2 = new Device();
device2.setDeviceId("device2");
device2.setDeviceName("Bedroom Curtain");

SwitchBotDeviceApi spyDeviceApi = spy(deviceApi);
doReturn(Arrays.asList(device1, device2)).when(spyDeviceApi).getDevices();

String result = spyDeviceApi.getDeviceNameFromId("device1");
assertEquals("Living Room Light", result);

String result2 = spyDeviceApi.getDeviceNameFromId("device2");
assertEquals("Bedroom Curtain", result2);
}

@Test
public void testGetDeviceNameFromIdWithUnknownDevice() throws IOException {
Device device = new Device();
device.setDeviceId("known-device");
device.setDeviceName("Known Device");

SwitchBotDeviceApi spyDeviceApi = spy(deviceApi);
doReturn(Arrays.asList(device)).when(spyDeviceApi).getDevices();

String result = spyDeviceApi.getDeviceNameFromId("unknown-device");
assertEquals("unknown-device", result);
}

@Test
public void testGetDeviceNameFromIdCacheExpiry() throws IOException {
Device device = new Device();
device.setDeviceId("device1");
device.setDeviceName("Test Device");

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

doReturn(Arrays.asList(device)).when(spyDeviceApi).getDevices();

String result = spyDeviceApi.getDeviceNameFromId("device1");
assertEquals("Test Device", result);

verify(spyDeviceApi, times(1)).getDevices();
}

@Test
public void testGetDeviceNameFromIdWithValidCache() throws IOException {
Device device = new Device();
device.setDeviceId("device1");
device.setDeviceName("Cached Device");

SwitchBotDeviceApi spyDeviceApi = spy(deviceApi);
spyDeviceApi.deviceIdToNamesCacheTime = System.currentTimeMillis();

doReturn(Arrays.asList(device)).when(spyDeviceApi).getDevices();
spyDeviceApi.getDeviceNameFromId("device1");

reset(spyDeviceApi);
spyDeviceApi.deviceIdToNamesCacheTime = System.currentTimeMillis();
spyDeviceApi.getDeviceNameFromId("device1");

verify(spyDeviceApi, never()).getDevices();
}

@Test
public void testGetDeviceNameFromIdWithIOException() throws IOException {
SwitchBotDeviceApi spyDeviceApi = spy(deviceApi);
doThrow(new IOException("Network error")).when(spyDeviceApi).getDevices();

String result = spyDeviceApi.getDeviceNameFromId("device1");
assertEquals("device1", result);
}

@Test
public void testGetDeviceStatusWithNullDeviceId() throws IOException {
Device result = deviceApi.getDeviceStatus(null);
assertNull(result);
}

@Test
public void testGetDeviceStatusInputValidation() {
assertDoesNotThrow(() -> {
assertNotNull(deviceApi);
});
}

@Test
public void testSendDeviceControlCommandsValidation() {
DeviceCommand command = new DeviceCommand("turnOn", "default");
when(mockSwitchBotApi.getMoshi()).thenReturn(new com.squareup.moshi.Moshi.Builder().build());

assertNotNull(command);
assertEquals("turnOn", command.getCommand());
assertEquals("default", command.getParameter());
}

@Test
public void testRefreshDeviceNameMapWithEmptyList() throws IOException {
SwitchBotDeviceApi spyDeviceApi = spy(deviceApi);
doReturn(Collections.emptyList()).when(spyDeviceApi).getDevices();

String result = spyDeviceApi.getDeviceNameFromId("any-device");
assertEquals("any-device", result);
}

@Test
public void testConcurrentCacheRefresh() throws InterruptedException {
SwitchBotDeviceApi spyDeviceApi = spy(deviceApi);
spyDeviceApi.deviceIdToNamesCacheTime = System.currentTimeMillis() - (ITimeConstants.HOUR * 2);

Device device = new Device();
device.setDeviceId("concurrent-device");
device.setDeviceName("Concurrent Test");
try {
doReturn(Arrays.asList(device)).when(spyDeviceApi).getDevices();
} catch (IOException e) {
fail("Setup failed: " + e.getMessage());
}

Thread thread1 = new Thread(() -> {
spyDeviceApi.getDeviceNameFromId("concurrent-device");
});

Thread thread2 = new Thread(() -> {
spyDeviceApi.getDeviceNameFromId("concurrent-device");
});

thread1.start();
thread2.start();

thread1.join();
thread2.join();

try {
verify(spyDeviceApi, atLeastOnce()).getDevices();
} catch (IOException e) {
fail("Verification failed: " + e.getMessage());
}
}
}
Loading