Skip to content

Commit d5bcc46

Browse files
author
Kristel
committed
integration test improvements
1 parent f7893a5 commit d5bcc46

23 files changed

+440
-205
lines changed

.github/workflows/verify.yml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
env:
3434
TADO_USERNAME: ${{ secrets.TADO_USERNAME }}
3535
TADO_PASSWORD: ${{ secrets.TADO_PASSWORD }}
36+
TADO_BRIDGE_AUTH_KEY: ${{ secrets.TADO_BRIDGE_AUTH_KEY }}
3637
run: mvn --batch-mode --update-snapshots clean verify -P local-api-spec --file pom.xml
3738

3839
# Building the site must be a separate step, otherwise no aggregated reports are available

scripts/set-secrets.sh.example

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
#!/bin/bash
22
export TADO_USERNAME='<fill in>'
3-
export TADO_PASSWORD='<fill in>'
3+
export TADO_PASSWORD='<fill in>'
4+
export TADO_BRIDGE_AUTH_KEY=<fill in>

tado-api-test/src/test/kotlin/tadoclient/TadoConfig.kt

+22
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ data class TadoConfig (
1717

1818
val mobileDevice: TadoObjectLongId?,
1919

20+
val bridge:TadoBridge?,
21+
22+
val installation:TadoObjectIntId?,
23+
24+
val boiler:TadoBoiler?,
25+
26+
val heatingCircuit: TadoObjectIntId?,
27+
2028
@field:NotNull
2129
var username:String,
2230

@@ -57,3 +65,17 @@ data class TadoConfigZoneHotWater (
5765
@field:NotNull
5866
val canSetTemperature: Boolean
5967
)
68+
69+
data class TadoBridge (
70+
@field:NotNull
71+
val id:String,
72+
73+
@field:NotNull
74+
val authKey:String
75+
)
76+
77+
data class TadoBoiler(
78+
// OPENTHERM or other (e.g. UBA_BUS)
79+
@field:NotNull
80+
val interfaceType:String
81+
)

tado-api-test/src/test/kotlin/tadoclient/apis/BaseTest.kt

+13
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,17 @@ open class BaseTest(val tadoConfig: TadoConfig) {
4343
fun isHomeAndMobileDeviceConfigured() : Boolean {
4444
return isHomeConfigured() && isMobileDeviceConfigured()
4545
}
46+
47+
fun isBridgeConfigured(): Boolean {
48+
// todo: implement
49+
return false
50+
}
51+
52+
fun isInstallationConfigured(): Boolean {
53+
return tadoConfig.installation != null
54+
}
55+
56+
fun isBoilerOpenThermInterface(): Boolean {
57+
return tadoConfig.boiler != null && tadoConfig.boiler.interfaceType.equals("OPENTHERM")
58+
}
4659
}

tado-api-test/src/test/kotlin/tadoclient/apis/BridgeApi_IT.kt

+9-22
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,30 @@ import tadoclient.Application
1010
import tadoclient.TadoConfig
1111
import tadoclient.verify.assertCorrectResponse
1212
import tadoclient.verify.verifyHeatingCircuit
13+
import tadoclient.verify.verifyObject
1314
import tadoclient.verify.verifyZoneControl
1415
import kotlin.test.assertNotEquals
1516

1617
@SpringBootTest(classes = arrayOf( Application::class))
1718
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
1819
@DisplayName("tado API - heating circuit")
19-
class HeatingCircuitAPI_IT(
20+
class BridgeApi_IT(
2021
@Qualifier("tadoStrictRestClient")
2122
val tadoStrictRestClient: RestClient,
2223

2324
@Autowired
2425
tadoConfig: TadoConfig
2526
): BaseTest(tadoConfig) {
26-
val tadoStrictHeatingCircuitApiAPI = HeatingCircuitApi(tadoStrictRestClient)
27+
val tadoStrictBridgeAPI = BridgeApi(tadoStrictRestClient)
2728

2829
@Test
29-
@DisplayName("GET /homes/{homeId}/heatingCircuits")
30+
@DisplayName("GET /bridges/{bridgeId}")
3031
@Order(10)
31-
@EnabledIf(value = "isHomeConfigured", disabledReason = "no home specified in tado set-up")
32+
@EnabledIf(value = "isBridgeConfigured", disabledReason = "no bridge specified in tado set-up")
3233
fun getHeatingCircuits() {
33-
val endpoint = "GET /homes/{homeId}/heatingCircuits"
34-
val heatingCircuits = assertCorrectResponse { tadoStrictHeatingCircuitApiAPI.getHeatingCircuits(tadoConfig.home!!.id) }
35-
assertNotEquals(0, heatingCircuits.size)
36-
heatingCircuits.forEachIndexed {i, elem -> verifyHeatingCircuit(elem, "$endpoint[$i]") }
37-
}
38-
39-
// Integration test for GET /homes/{homeId}/zones/{zoneId}/control
40-
// see DeviceApi_IT
41-
// (the operation belongs to multiple API operation groups, and the 'device' group is considered to be the primary one)
42-
43-
@Test
44-
@DisplayName("GET /homes/{homeId}/zones/{zoneId}/control/heatingCircuit")
45-
@Order(20)
46-
@EnabledIf(value = "isHeatingZoneConfigured", disabledReason = "no home specified in tado set-up")
47-
fun setHeatingCircuitForZone() {
48-
val endpoint = "PUT /homes/{homeId}/zones/{zoneId}/control/heatingCircuit"
49-
val zoneControl = assertCorrectResponse { tadoStrictHeatingCircuitApiAPI.setHeatingCircuit(tadoConfig.home!!.id, tadoConfig.zone!!.heating!!.id) }
50-
verifyZoneControl(zoneControl, endpoint) }
34+
val endpoint = "GET /bridges/{bridgeId}"
35+
val bridge = assertCorrectResponse { tadoStrictBridgeAPI.getBridge(tadoConfig.bridge!!.id, tadoConfig.bridge.authKey) }
36+
val typeName = "Bridge"
37+
verifyObject(bridge, endpoint, typeName, typeName)
5138
}
5239
}

tado-api-test/src/test/kotlin/tadoclient/apis/DeviceApi_IT.kt

+46-18
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ import org.springframework.boot.test.context.SpringBootTest
88
import org.springframework.web.client.RestClient
99
import tadoclient.Application
1010
import tadoclient.TadoConfig
11+
import tadoclient.models.ChildLock
12+
import tadoclient.models.MoveDeviceRequest
1113
import tadoclient.models.Temperature
12-
import tadoclient.verify.assertCorrectResponse
13-
import tadoclient.verify.verifyDevice
14-
import tadoclient.verify.verifyDeviceListItem
15-
import tadoclient.verify.verifyNested
14+
import tadoclient.verify.*
1615
import kotlin.test.Test
1716
import kotlin.test.assertEquals
1817
import kotlin.test.assertNotEquals
@@ -24,10 +23,14 @@ class DeviceApi_IT (
2423
@Qualifier("tadoStrictRestClient")
2524
val tadoStrictRestClient: RestClient,
2625

26+
@Qualifier("tadoRestClient")
27+
val tadoRestClient: RestClient,
28+
2729
@Autowired
2830
tadoConfig: TadoConfig
2931
): BaseTest(tadoConfig) {
3032
val tadoStrictDeviceAPI = DeviceApi(tadoStrictRestClient)
33+
val tadoDeviceAPI = DeviceApi(tadoRestClient)
3134

3235
@Test
3336
@DisplayName("GET /devices/{deviceId}")
@@ -39,6 +42,17 @@ class DeviceApi_IT (
3942
verifyDevice(device, endpoint)
4043
}
4144

45+
@Test
46+
@DisplayName("PUT /devices/{deviceId}/childLock")
47+
@Order(5)
48+
@EnabledIf(value = "isThermostatDeviceConfigured", disabledReason = "no thermostat device specified in tado set-up")
49+
fun setChildLock() {
50+
// first get the current child lock setting
51+
val device = tadoDeviceAPI.getDevice(tadoConfig.device!!.thermostat!!.id)
52+
// then test by re-setting the same childLock setting
53+
tadoStrictDeviceAPI.setChildLock(tadoConfig.device!!.thermostat!!.id, ChildLock(device.childLockEnabled))
54+
}
55+
4256
@Test
4357
@DisplayName("POST /devices/{deviceId}/identify")
4458
@Order(10)
@@ -55,18 +69,18 @@ class DeviceApi_IT (
5569
val endpoint = "GET /devices/{deviceId}/temperatureOffset"
5670
val offset = assertCorrectResponse { tadoStrictDeviceAPI.getTemperatureOffset(tadoConfig.device!!.thermostat!!.id) }
5771
val typeName = "TemperatureOffset"
58-
verifyNested(offset, endpoint, typeName, typeName)
72+
verifyObject(offset, endpoint, typeName, typeName)
5973
}
6074

6175
@Test
6276
@DisplayName("PUT /devices/{deviceId}/temperatureOffset")
6377
@Order(30)
6478
@EnabledIf(value = "isThermostatDeviceConfigured", disabledReason = "no thermostat device specified in tado set-up")
6579
fun putTemperatureOffset() {
66-
val endpoint = "OYT /devices/{deviceId}/temperatureOffset"
80+
val endpoint = "PUT /devices/{deviceId}/temperatureOffset"
6781
val offset = assertCorrectResponse { tadoStrictDeviceAPI.setTemperatureOffset(tadoConfig.device!!.thermostat!!.id, Temperature(0.5f)) }
6882
val typeName = "TemperatureOffset"
69-
verifyNested(offset, endpoint, typeName, typeName)
83+
verifyObject(offset, endpoint, typeName, typeName)
7084
}
7185

7286
@Test
@@ -107,11 +121,18 @@ class DeviceApi_IT (
107121
fun getInstallations() {
108122
val endpoint = "GET /homes/{homeId}/installations"
109123
val installations = assertCorrectResponse { tadoStrictDeviceAPI.getInstallations(tadoConfig.home!!.id) }
124+
// observation: returned an empty array until an aircon controller was added to the home
125+
installations.forEachIndexed { i, elem -> verifyInstallation(elem, endpoint, "response[$i]") }
126+
}
110127

111-
// returns AC installations
112-
assertEquals(1, installations.size)
113-
114-
// todo: implement an verifyInstallation method
128+
@Test
129+
@DisplayName("GET /homes/{homeId}/installations/{installationId}")
130+
@Order(66)
131+
@EnabledIf(value = "isInstallationConfigured", disabledReason = "no home specified in tado set-up")
132+
fun getInstallation() {
133+
val endpoint = "GET /homes/{homeId}/installations/{installationId}"
134+
val installation = assertCorrectResponse { tadoStrictDeviceAPI.getInstallation(tadoConfig.home!!.id, tadoConfig.installation!!.id) }
135+
verifyInstallation(installation, endpoint)
115136
}
116137

117138
@Test
@@ -123,7 +144,7 @@ class DeviceApi_IT (
123144
val zoneControl = assertCorrectResponse { tadoStrictDeviceAPI.getZoneControl(tadoConfig.home!!.id, tadoConfig.zone!!.heating!!.id) }
124145

125146
val typeName = "ZoneControl"
126-
verifyNested(zoneControl, endpoint, endpoint, typeName,
147+
verifyObject(zoneControl, endpoint, endpoint, typeName,
127148
nullAllowedProperties = listOf(
128149
"$typeName.duties.driver",
129150
"$typeName.duties.drivers",
@@ -134,12 +155,14 @@ class DeviceApi_IT (
134155
)
135156
}
136157

158+
// integration test for "GET /homes/{homeId}/zones/{zoneId}/control"
159+
// see HeatingCircuitAPI_IT
160+
137161
@Test
138-
@DisplayName("PUT /homes/{homeId}/zones/{zoneId}/control/heatingCircuit")
162+
@DisplayName("POST /homes/{homeId}/zones/{zoneId}/devices")
139163
@Order(80)
140-
@Disabled("to be implemented")
141-
fun putControl() {
142-
// TODO: to be implemented
164+
@Disabled("needs more analysis")
165+
fun moveDevice() {
143166
}
144167

145168
@Test
@@ -155,9 +178,14 @@ class DeviceApi_IT (
155178
@Test
156179
@DisplayName("PUT /homes/{homeId}/zones/{zoneId}/measuringDevice")
157180
@Order(100)
158-
@Disabled("not yet implemented")
181+
@EnabledIf(value = "isHomeAndHeatingZoneConfigured", disabledReason = "no home specified in tado set-up")
159182
fun putMeasuringDevice() {
160-
//TODO: to be implemented
183+
// first get the current measuring device and simply put the same one again
184+
val measuringDevice = tadoDeviceAPI.getZoneMeasuringDevice(tadoConfig.home!!.id, tadoConfig.zone!!.heating!!.id)
185+
// now test
186+
val endpoint = "PUT /homes/{homeId}/zones/{zoneId}/measuringDevice"
187+
val device = assertCorrectResponse { tadoStrictDeviceAPI.setZoneMeasuringDevice(tadoConfig.home.id, tadoConfig.zone.heating!!.id, MoveDeviceRequest(measuringDevice.serialNo)) }
188+
verifyDevice(device, endpoint)
161189
}
162190

163191
}

tado-api-test/src/test/kotlin/tadoclient/apis/HeatingCircuitAPI_IT.kt

+31
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import org.springframework.boot.test.context.SpringBootTest
88
import org.springframework.web.client.RestClient
99
import tadoclient.Application
1010
import tadoclient.TadoConfig
11+
import tadoclient.models.HeatingCircuitInput
1112
import tadoclient.verify.assertCorrectResponse
1213
import tadoclient.verify.verifyHeatingCircuit
14+
import tadoclient.verify.verifyZoneControl
1315
import kotlin.test.assertNotEquals
1416

1517
@SpringBootTest(classes = arrayOf( Application::class))
@@ -19,10 +21,14 @@ class HeatingCircuitAPI_IT(
1921
@Qualifier("tadoStrictRestClient")
2022
val tadoStrictRestClient: RestClient,
2123

24+
@Qualifier("tadoRestClient")
25+
val tadoRestClient: RestClient,
26+
2227
@Autowired
2328
tadoConfig: TadoConfig
2429
): BaseTest(tadoConfig) {
2530
val tadoStrictHeatingCircuitApiAPI = HeatingCircuitApi(tadoStrictRestClient)
31+
val tadoDeviceAPI = DeviceApi(tadoRestClient)
2632

2733
@Test
2834
@DisplayName("GET /homes/{homeId}/heatingCircuits")
@@ -35,4 +41,29 @@ class HeatingCircuitAPI_IT(
3541
heatingCircuits.forEachIndexed {i, elem -> verifyHeatingCircuit(elem, "$endpoint[$i]") }
3642
}
3743

44+
// Integration test for GET /homes/{homeId}/zones/{zoneId}/control
45+
// see DeviceApi_IT
46+
// (the operation belongs to multiple API operation groups, and the 'device' group is considered to be the primary one)
47+
48+
@Test
49+
@DisplayName("PUT /homes/{homeId}/zones/{zoneId}/control/heatingCircuit")
50+
@Order(20)
51+
@EnabledIf(value = "isHeatingZoneConfigured", disabledReason = "no home specified in tado set-up")
52+
fun setHeatingCircuitForZone() {
53+
// first get the zones current heating circuit
54+
val currentZoneControl = tadoDeviceAPI.getZoneControl(tadoConfig.home!!.id, tadoConfig.zone!!.heating!!.id)
55+
// only continue when this zone has a heating circuit
56+
currentZoneControl.heatingCircuit?.let {
57+
val endpoint = "PUT /homes/{homeId}/zones/{zoneId}/control/heatingCircuit"
58+
val input = HeatingCircuitInput(currentZoneControl.heatingCircuit)
59+
val zoneControl = assertCorrectResponse {
60+
tadoStrictHeatingCircuitApiAPI.setHeatingCircuit(
61+
tadoConfig.home.id,
62+
tadoConfig.zone.heating!!.id,
63+
input
64+
)
65+
}
66+
verifyZoneControl(zoneControl, endpoint)
67+
}
68+
}
3869
}

tado-api-test/src/test/kotlin/tadoclient/apis/HomeApi_IT.kt

+25-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import tadoclient.Application
1010
import tadoclient.TadoConfig
1111
import tadoclient.models.*
1212
import tadoclient.verify.*
13+
import java.math.BigDecimal
1314
import kotlin.test.Test
1415
import kotlin.test.assertEquals
1516

@@ -103,6 +104,28 @@ class HomeApi_IT(
103104
assertCorrectResponse { tadoStrictHomeAPI.setHomeDetails(tadoConfig.home!!.id, homeDetails) }
104105
}
105106

107+
@Test
108+
@DisplayName("GET /homes/{homeId}/flowTemperatureOptimization")
109+
@Order(40)
110+
@EnabledIf(value = "isBoilerOpenThermInterface", disabledReason = "boiler does not support OPENTHERM")
111+
fun getFlowTemperaturOptimization() {
112+
val endpoint = "GET /homes/{homeId}/flowTemperatureOptimization"
113+
val flowTempOptimization = assertCorrectResponse { tadoStrictHomeAPI.getFlowTemperatureOptimization(tadoConfig.home!!.id) }
114+
val typeName = "FlowTemperatureOptimization"
115+
verifyObject(flowTempOptimization, endpoint, typeName, typeName,
116+
nullAllowedProperties = listOf("$typeName.autoAdaptation.maxFlowTemperature")
117+
)
118+
}
119+
120+
@Test
121+
@DisplayName("PUT /homes/{homeId}/flowTemperatureOptimization")
122+
@Order(41)
123+
@Disabled("not included in weekly automated test") // and boiler also needs to support OPENTHERM
124+
fun putFlowTemperaturOptimization() {
125+
val input = FlowTemperatureOptimizationInput(BigDecimal(50.5))
126+
tadoStrictHomeAPI.setFlowTemperatureOptimization(tadoConfig.home!!.id, input)
127+
}
128+
106129
@Test
107130
@DisplayName("GET /homes/{homeId}/heatingSystem")
108131
@Order(50)
@@ -158,7 +181,7 @@ class HomeApi_IT(
158181
val endpoint = "GET /homes/{homeId}/incidentDetection"
159182
val incidentDetection = assertCorrectResponse { tadoStrictHomeAPI.getIncidentDetection(tadoConfig.home!!.id) }
160183
val typeName = "IncidentDetection"
161-
verifyNested(incidentDetection, endpoint, typeName, typeName)
184+
verifyObject(incidentDetection, endpoint, typeName, typeName)
162185
}
163186

164187
@Test
@@ -172,7 +195,7 @@ class HomeApi_IT(
172195
@Test
173196
@DisplayName("GET /homes/{homeId}/invitations")
174197
@Order(95)
175-
@Disabled("not yet available in spec")
198+
@Disabled("not yet defined in API spec")
176199
fun getInvitations() {
177200
// TODO: implement once available in the spec
178201
}

0 commit comments

Comments
 (0)