Skip to content

Commit e0975ed

Browse files
author
Kristel
committed
integration test improvements
1 parent d559fd1 commit e0975ed

File tree

8 files changed

+98
-69
lines changed

8 files changed

+98
-69
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ class DeviceApi_IT (
161161
@Test
162162
@DisplayName("POST /homes/{homeId}/zones/{zoneId}/devices")
163163
@Order(80)
164-
@Disabled("needs more analysis")
164+
@Disabled("Unsuitable for weekly automated test execution: moving a device to another zone")
165165
fun moveDevice() {
166166
}
167167

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

+6-12
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,12 @@ class HomeApi_IT(
7676
@Test
7777
@DisplayName("PUT /homes/{homeId}/awayRadiusInMeters")
7878
@Order(25)
79-
@Disabled("not yet available in spec")
79+
@EnabledIf(value = "isHomeConfigured", disabledReason = "no home specified in tado set-up")
8080
fun putAwayRadiusInMeters() {
81-
// TODO: implement once available in spec
81+
// first get the current value
82+
val currentRadius = tadoHomeAPI.getHome(tadoConfig.home!!.id).awayRadiusInMeters
83+
// then do the actual test
84+
tadoStrictHomeAPI.setAwayRadiusInMeters(tadoConfig.home.id, AwayRadiusInput(currentRadius))
8285
}
8386

8487
@Test
@@ -120,7 +123,7 @@ class HomeApi_IT(
120123
@Test
121124
@DisplayName("PUT /homes/{homeId}/flowTemperatureOptimization")
122125
@Order(41)
123-
@Disabled("not included in weekly automated test") // and boiler also needs to support OPENTHERM
126+
@Disabled("Unsuitable for weekly automated test execution: not making any changes to the boiler") // and boiler also needs to support OPENTHERM
124127
fun putFlowTemperaturOptimization() {
125128
val input = FlowTemperatureOptimizationInput(BigDecimal(50.5))
126129
tadoStrictHomeAPI.setFlowTemperatureOptimization(tadoConfig.home!!.id, input)
@@ -192,14 +195,6 @@ class HomeApi_IT(
192195
assertCorrectResponse { tadoStrictHomeAPI.setIncidentDetection(tadoConfig.home!!.id, IncidentDetectionInput(enabled = true)) }
193196
}
194197

195-
@Test
196-
@DisplayName("GET /homes/{homeId}/invitations")
197-
@Order(95)
198-
@Disabled("not yet defined in API spec")
199-
fun getInvitations() {
200-
// TODO: implement once available in the spec
201-
}
202-
203198
@Test
204199
@DisplayName("GET /homes/{homeId}/weather")
205200
@Order(100)
@@ -209,5 +204,4 @@ class HomeApi_IT(
209204
val weather = assertCorrectResponse { tadoStrictHomeAPI.getWeather(tadoConfig.home!!.id) }
210205
verifyWeather(weather, endpoint)
211206
}
212-
213207
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class HomeByBridgeApi_IT(
5656
@Test
5757
@DisplayName("PUT /homeByBridge/{bridgeId}/boilerMaxOutputTemperature")
5858
@Order(30)
59-
@Disabled("no tests executed which impact the boiler")
59+
@Disabled("Unsuitable for weekly automated test execution: not making any changes to the boiler")
6060
fun putBoilerMaxOutputTemperature() {
6161
tadoStrictHomeByBridgeApi.setBoilerMaxOutputTemperature(tadoConfig.bridge!!.id, tadoConfig.bridge.authKey, BoilerMaxOutputTemperature(55f))
6262
}
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
package tadoclient.apis
22

3-
import org.junit.jupiter.api.DisplayName
4-
import org.junit.jupiter.api.MethodOrderer
5-
import org.junit.jupiter.api.Order
6-
import org.junit.jupiter.api.TestMethodOrder
3+
import org.junit.jupiter.api.*
74
import org.junit.jupiter.api.condition.EnabledIf
85
import org.springframework.beans.factory.annotation.Autowired
96
import org.springframework.beans.factory.annotation.Qualifier
107
import org.springframework.boot.test.context.SpringBootTest
118
import org.springframework.web.client.RestClient
129
import tadoclient.Application
1310
import tadoclient.TadoConfig
11+
import tadoclient.models.InvitationRequest
1412
import tadoclient.verify.assertCorrectResponse
15-
import tadoclient.verify.verifyUser
13+
import tadoclient.verify.verifyObject
1614
import kotlin.test.Test
17-
import kotlin.test.assertNotEquals
1815

1916
@SpringBootTest(classes = arrayOf( Application::class))
2017
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
2118
@DisplayName("tado API - user")
22-
class UserApi_IT (
19+
class InvitationApi_IT (
2320
// rest client to use when not testing an API method
2421
@Qualifier("tadoRestClient")
2522
val tadoRestClient: RestClient,
@@ -32,27 +29,66 @@ class UserApi_IT (
3229
@Autowired
3330
tadoConfig: TadoConfig
3431
) : BaseTest(tadoConfig) {
35-
val tadoStrictUserAPI = UserApi(tadoStrictRestClient)
32+
val tadoStrictInvitationAPI = InvitationApi(tadoStrictRestClient)
33+
34+
// We save the token of the invite we are creating, so we can re-use it in the
35+
// resend and revoke operations
36+
// This only works because we define the order in which the test methods should be executed
37+
var createdInvitationToken:String? = null
3638

3739
@Test
38-
@DisplayName("GET /me")
40+
@DisplayName("POST /homes/{homeId}/invitations")
3941
@Order(10)
40-
fun getMe() {
41-
val endpoint = "GET /me"
42-
val user = assertCorrectResponse { tadoStrictUserAPI.getMe() }
43-
verifyUser(user, endpoint)
42+
@EnabledIf(value = "isHomeConfigured", disabledReason = "no home specified in tado set-up")
43+
fun sendInvitation() {
44+
val endpoint = "POST /homes/{homeId}/invitations"
45+
val invitation = assertCorrectResponse { tadoStrictInvitationAPI.sendInvitation(tadoConfig.home!!.id, InvitationRequest("[email protected]")) }
46+
verifyObject(invitation, endpoint, endpoint, "Invitation")
47+
this.createdInvitationToken = invitation.token
4448
}
4549

4650
@Test
47-
@DisplayName("GET /homes/{homeId}/users")
51+
@DisplayName("POST /homes/{homeId}/invitations/{invitationToken}")
4852
@Order(20)
4953
@EnabledIf(value = "isHomeConfigured", disabledReason = "no home specified in tado set-up")
50-
fun getUsers() {
51-
val endpoint = "GET /homes/{homeId}/users"
52-
val users = assertCorrectResponse { tadoStrictUserAPI.getUsers(tadoConfig.home!!.id) }
54+
fun resendInvitation() {
55+
if (createdInvitationToken != null) {
56+
val endpoint = "POST /homes/{homeId}/invitations/{invitationToken}"
57+
val invitation = assertCorrectResponse {
58+
tadoStrictInvitationAPI.resendInvitation(
59+
tadoConfig.home!!.id,
60+
createdInvitationToken!!
61+
)
62+
}
63+
verifyObject(invitation, endpoint, endpoint, "Invitation")
64+
} else {
65+
fail("no invitation created, so there is no invitation to resend")
66+
}
67+
}
5368

54-
// check users
55-
assertNotEquals(0, users.size)
56-
users.forEachIndexed{i, elem -> verifyUser(elem, endpoint, "response[$i]")}
69+
@Test
70+
@DisplayName("GET /homes/{homeId}/invitations")
71+
@Order(30)
72+
@EnabledIf(value = "isHomeConfigured", disabledReason = "no home specified in tado set-up")
73+
fun getInvitations() {
74+
if (createdInvitationToken != null) {
75+
val endpoint = "GET /homes/{homeId}/invitations"
76+
val invitations = assertCorrectResponse { tadoStrictInvitationAPI.getInvitations(tadoConfig.home!!.id) }
77+
invitations.forEachIndexed{i, elem -> verifyObject(elem, endpoint, endpoint, "response[$i]")}
78+
} else {
79+
fail("no invitation created, so there are no invitations to retrieve")
80+
}
81+
}
82+
83+
@Test
84+
@DisplayName("DELETE /homes/{homeId}/invitations/{invitationToken}")
85+
@Order(40)
86+
@EnabledIf(value = "isHomeConfigured", disabledReason = "no home specified in tado set-up")
87+
fun revokeInvitation() {
88+
if (createdInvitationToken != null) {
89+
tadoStrictInvitationAPI.revokeInvitation(tadoConfig.home!!.id, createdInvitationToken!!)
90+
} else {
91+
fail("no invitation created, so there is no invitation to revoke")
92+
}
5793
}
5894
}

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ import kotlin.test.Test
1616
import kotlin.test.assertNotEquals
1717
import kotlin.test.assertNotNull
1818

19-
/**
20-
* Not tested: DELETE /homes/{homeId}/mobileDevices/{mobileDeviceId} as it would be a destructive test
21-
*/
22-
2319
@SpringBootTest(classes = arrayOf( Application::class))
2420
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
2521
@DisplayName("tado API - mobile device")
@@ -65,7 +61,7 @@ class MobileDeviceApi_IT(
6561
@Test
6662
@DisplayName("DELETE /homes/{homeId}/mobileDevices/{mobileDeviceId}")
6763
@Order(30)
68-
@Disabled("Skipped because of destructive nature of the testcase (and the fact that it is non-trivial to recreate the initial situation)")
64+
@Disabled("Unsuitable for weekly automated test execution: because of destructive nature of the testcase which is non-trivial to undo programmatically)")
6965
fun deleteMobileDevice() {
7066
// no implementation
7167
}

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

+24-22
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class ZoneApi_IT(
5858
@Test
5959
@DisplayName("POST /homes/{homeId}/zones")
6060
@Order(15)
61-
@Disabled("needs more investigation")
61+
@Disabled("Unsuitable for weekly automated test execution: moving a device to a new zone")
6262
fun createZoneAndMoveDevice() {
6363
val endpoint = "POST /homes/{homeId}/zones"
6464
val input = ZoneCreate("IMPLICIT_CONTROL", ZoneType.HEATING, listOf(MoveDeviceRequest("xxx")))
@@ -104,7 +104,7 @@ class ZoneApi_IT(
104104

105105
@Test
106106
@DisplayName("PUT /homes/{homeId}/zones/{zoneId}/dazzle")
107-
@Order(25)
107+
@Order(30)
108108
@EnabledIf(value="isHeatingZoneConfigured", disabledReason = "no heating zone configured")
109109
fun putDazzle() {
110110
val input = DazzleInput(true)
@@ -113,7 +113,7 @@ class ZoneApi_IT(
113113

114114
@Test
115115
@DisplayName("PUT /homes/{homeId}/zones/{zoneId}/dazzle")
116-
@Order(27)
116+
@Order(35)
117117
@EnabledIf(value="isHeatingZoneConfigured", disabledReason = "no heating zone configured")
118118
fun putDetails() {
119119
// set the zone's current name
@@ -123,7 +123,7 @@ class ZoneApi_IT(
123123

124124
@Test
125125
@DisplayName("PUT /homes/{homeId}/zones/{zoneId}/openWindowDetection")
126-
@Order(28)
126+
@Order(40)
127127
@EnabledIf(value="isHeatingZoneConfigured", disabledReason = "no heating zone configured")
128128
fun putOpenWindowDetection() {
129129
// set the zone's current open window detection settings
@@ -136,24 +136,26 @@ class ZoneApi_IT(
136136

137137
@Test
138138
@DisplayName("POST /homes/{homeId}/zones/{zoneId}/state/openWindow/activate")
139-
@Order(29)
140-
@Disabled("needs more investigation")
139+
@Order(41)
140+
@EnabledIf(value="isHeatingZoneConfigured", disabledReason = "no heating zone configured")
141141
fun activateOpenWindowStateForZone() {
142-
tadoStrictZoneAPI.activateOpenWindowState(tadoConfig.home!!.id, tadoConfig.zone!!.heating!!.id)
142+
// no idea what this operation actually does
143+
tadoStrictZoneAPI.activateOpenWindowState(tadoConfig.home!!.id, tadoConfig.zone!!.heating!!.id)
143144
}
144145

145146
@Test
146147
@DisplayName("DELETE /homes/{homeId}/zones/{zoneId}/state/openWindow")
147-
@Order(30)
148-
@Disabled("needs more investigation")
148+
@Order(42)
149+
@EnabledIf(value="isHeatingZoneConfigured", disabledReason = "no heating zone configured")
149150
fun deleteOpenWindowStateForZone() {
151+
// no idea what this operation actually does
150152
tadoStrictZoneAPI.deactivateOpenWindowState(tadoConfig.home!!.id, tadoConfig.zone!!.heating!!.id)
151153
}
152154

153155
@Test
154156
@DisplayName("GET /homes/{homeId}/zones/{zoneId}/state - AIR_CONDITIONING")
155157
@EnabledIf(value = "isAirConZoneConfigured", disabledReason = "no AIR_CONDITIONING zone specified in tado set-up")
156-
@Order(30)
158+
@Order(50)
157159
fun getZoneState_AirCon() {
158160
val endpoint = "GET /homes/{homeId}/zones/{zoneId}/state"
159161
val zoneState = assertCorrectResponse { tadoStrictZoneAPI.getZoneState(tadoConfig.home!!.id, tadoConfig.zone!!.airCon!!.id) }
@@ -163,7 +165,7 @@ class ZoneApi_IT(
163165
@Test
164166
@DisplayName("GET /homes/{homeId}/zones/{zoneId}/state - HEATING")
165167
@EnabledIf(value = "isHomeAndHeatingZoneConfigured", disabledReason = "no home and/or HEATING zone specified in tado set-up")
166-
@Order(31)
168+
@Order(51)
167169
fun getZoneState_Heating() {
168170
val endpoint = "GET /homes/{homeId}/zones/{zoneId}/state"
169171
val zoneState = assertCorrectResponse { tadoStrictZoneAPI.getZoneState(tadoConfig.home!!.id, tadoConfig.zone!!.heating!!.id) }
@@ -173,7 +175,7 @@ class ZoneApi_IT(
173175
@Test
174176
@DisplayName("GET /homes/{homeId}/zones/{zoneId}/state - HOT_WATER")
175177
@EnabledIf(value = "isHomeAndHotWaterZoneConfigured", disabledReason = "no home and/or HOT_WATER zone specified in tado set-up")
176-
@Order(32)
178+
@Order(52)
177179
fun getZoneState_HotWater() {
178180
val endpoint = "GET /homes/{homeId}/zones/{zoneId}/state"
179181
val zoneState = assertCorrectResponse { tadoStrictZoneAPI.getZoneState(tadoConfig.home!!.id, tadoConfig.zone!!.hotWater!!.id) }
@@ -182,23 +184,15 @@ class ZoneApi_IT(
182184

183185
@Test
184186
@DisplayName("GET /homes/{homeId}/zones/{zoneId}/state - 404 (unknown zoneId)")
185-
@Order(33)
187+
@Order(53)
186188
@EnabledIf(value = "isHomeConfigured", disabledReason = "no home specified in tado set-up")
187189
fun getZoneState_404() {
188190
assertHttpErrorStatus(HttpStatus.NOT_FOUND) { tadoStrictZoneAPI.getZoneState(tadoConfig.home!!.id, 99999) }
189191
}
190192

191-
@Test
192-
@DisplayName("PUT /homes/{homeId}/zoneOrder")
193-
@Order(35)
194-
@Disabled("not yet available in spec")
195-
fun putZoneOrder() {
196-
// TODO: implement once available in spec
197-
}
198-
199193
@Test
200194
@DisplayName("GET /homes/{homeId}/zoneStates")
201-
@Order(40)
195+
@Order(55)
202196
@EnabledIf(value = "isHomeConfigured", disabledReason = "no home specified in tado set-up")
203197
fun getZoneStates() {
204198
val endpoint = "GET /homes/{homeId}/zoneStates"
@@ -214,5 +208,13 @@ class ZoneApi_IT(
214208
verifyZoneState(zoneStates.zoneStates?.get(tadoConfig.zone!!.airCon!!.id.toString())!!, endpoint, ancestorObjectProps = mapOf(ZONE_TYPE to ZoneType.AIR_CONDITIONING))
215209
}
216210
}
211+
212+
@Test
213+
@DisplayName("PUT /homes/{homeId}/zoneOrder")
214+
@Order(60)
215+
@Disabled("not yet available in API definition")
216+
fun putZoneOrder() {
217+
// TODO: implement once available in API definition
218+
}
217219

218220
}

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,8 @@ class ZoneControlApi_IT(
343343
@Test
344344
@DisplayName("PUT /homes/{homeId}/zones/{zoneId}/overlay - TIMER ON")
345345
@Order(64)
346-
//@EnabledIf(value = "isHomeAndHeatingZoneConfigured", disabledReason = "no home and/or HEATING zone specified in tado set-up")
347-
@Disabled("Disable due to strange error error response")
348-
// ZoneControlApi_IT.setZoneOverlay_timer:396 » Conflict 409 Conflict:
349-
// "{"errors":[{"code":"persistentEntityConflict","title":"conflict occurred while trying to update entity null"}]}"
346+
@EnabledIf(value = "isHomeAndHeatingZoneConfigured", disabledReason = "no home and/or HEATING zone specified in tado set-up")
347+
// @Disabled("Disable due to strange error error response")
350348
fun setZoneOverlay_timer() {
351349
val endpoint = "PUT /homes/{homeId}/zones/{zoneId}/overlay"
352350
// first delete any existing overlay

tado-api-test/src/test/kotlin/tadoclient/verify/HomeAPI_verify.kt

+7-4
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ fun verifyHome(home:Home, context:String, parentName:String = "Home", ancestorOb
2222
fun verifyAirComfort(airComfort: AirComfort, context:String, parentName:String = "AirComfort", ancestorObjectProps:Map<String, Any> = emptyMap()) {
2323
val typeName = "AirComfort"
2424
verifyObject(airComfort, context, parentName, typeName, ancestorObjectProps,
25-
// Properties below are not present when there is (temporarily) no connection with the measuring device in the room
25+
// The first thrSome of the properties below are not present when there is (temporarily) no connection with the measuring device in the room
2626
// (e.g because of an empty device battery).
2727
nullAllowedProperties = listOf(
28-
"$typeName.comfort[i].temperatureLevel",
29-
"$typeName.comfort[i].humidityLevel",
30-
"$typeName.comfort[i].coordinate"))
28+
"$typeName.comfort[i].temperatureLevel", // may be temporarily unavailable when no connection to measuring device
29+
"$typeName.comfort[i].humidityLevel", // may be temporarily unavailable when no connection to measuring device
30+
"$typeName.comfort[i].coordinate", // may be temporarily unavailable when no connection to measuring device
31+
"$typeName.comfort[i].acControl", // only available when a tado airco controller is part of the set-up
32+
"$typeName.comfort[i].lastAcPowerOff", // only available when a tado airco controller is part of the set-up
33+
))
3134
}
3235

3336
fun verifyHeatingSystem(heatingSystem: HeatingSystem, context:String, parentName:String = "HeatingSystem", ancestorObjectProps:Map<String, Any> = emptyMap()) {

0 commit comments

Comments
 (0)