github.com/Financial-Times/publish-availability-monitor@v1.12.0/checks/publishCheck_test.go (about) 1 package checks 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "net/http" 8 "net/url" 9 "testing" 10 "time" 11 12 "github.com/Financial-Times/go-logger/v2" 13 "github.com/Financial-Times/publish-availability-monitor/httpcaller" 14 "github.com/Financial-Times/publish-availability-monitor/metrics" 15 "github.com/stretchr/testify/assert" 16 ) 17 18 func TestIsCurrentOperationFinished_ContentCheck_InvalidContent(t *testing.T) { 19 currentTid := "tid_1234" 20 testResponse := `{ "uuid" : "1234-1234"` 21 response := buildResponse(200, testResponse) 22 defer response.Body.Close() 23 contentCheck := &ContentCheck{ 24 mockHTTPCaller(t, "tid_pam_1234", response), 25 } 26 log := logger.NewUPPLogger("test", "PANIC") 27 28 pm := newPublishMetricBuilder().withTID(currentTid).build() 29 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 30 pm, 31 "", 32 "", 33 0, 34 0, 35 nil, 36 nil, 37 log, 38 )) 39 assert.False(t, finished, "Expected error.") 40 } 41 42 func TestIsCurrentOperationFinished_ContentCheck_Finished(t *testing.T) { 43 currentTid := "tid_1234" 44 testResponse := fmt.Sprintf(`{ "uuid" : "1234-1234", "publishReference" : "%s"}`, currentTid) 45 response := buildResponse(200, testResponse) 46 defer response.Body.Close() 47 contentCheck := &ContentCheck{ 48 mockHTTPCaller(t, "tid_pam_1234", response), 49 } 50 log := logger.NewUPPLogger("test", "PANIC") 51 52 pm := newPublishMetricBuilder().withTID(currentTid).build() 53 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 54 pm, 55 "", 56 "", 57 0, 58 0, 59 nil, 60 nil, 61 log, 62 )) 63 assert.True(t, finished, "operation should have finished successfully") 64 } 65 66 func TestIsCurrentOperationFinished_ContentCheck_WithAuthentication(t *testing.T) { 67 currentTid := "tid_5678" 68 testResponse := fmt.Sprintf(`{ "uuid" : "1234-1234", "publishReference" : "%s"}`, currentTid) 69 username := "jdoe" 70 password := "frodo" 71 response := buildResponse(200, testResponse) 72 defer response.Body.Close() 73 contentCheck := &ContentCheck{ 74 mockAuthenticatedHTTPCaller(t, "tid_pam_5678", username, password, response), 75 } 76 log := logger.NewUPPLogger("test", "PANIC") 77 78 pm := newPublishMetricBuilder().withTID(currentTid).build() 79 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 80 pm, 81 username, 82 password, 83 0, 84 0, 85 nil, 86 nil, 87 log, 88 )) 89 assert.True(t, finished, "operation should have finished successfully") 90 } 91 92 func TestIsCurrentOperationFinished_ContentCheck_NotFinished(t *testing.T) { 93 currentTid := "tid_1234" 94 testResponse := `{ "uuid" : "1234-1234", "publishReference" : "tid_1235"}` 95 response := buildResponse(200, testResponse) 96 defer response.Body.Close() 97 contentCheck := &ContentCheck{ 98 mockHTTPCaller(t, "tid_pam_1234", response), 99 } 100 log := logger.NewUPPLogger("test", "PANIC") 101 102 pm := newPublishMetricBuilder().withTID(currentTid).build() 103 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 104 pm, 105 "", 106 "", 107 0, 108 0, 109 nil, 110 nil, 111 log, 112 )) 113 assert.False(t, finished, "Expected failure.") 114 } 115 116 func TestIsCurrentOperationFinished_ContentCheck_MarkedDeleted_Finished(t *testing.T) { 117 currentTid := "tid_1234" 118 testResponse := fmt.Sprintf(`{ "uuid" : "1234-1234", "publishReference" : "%s"}`, currentTid) 119 response := buildResponse(404, testResponse) 120 defer response.Body.Close() 121 contentCheck := &ContentCheck{ 122 mockHTTPCaller(t, "tid_pam_1234", response), 123 } 124 log := logger.NewUPPLogger("test", "PANIC") 125 126 pm := newPublishMetricBuilder().withTID(currentTid).withMarkedDeleted(true).build() 127 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 128 pm, 129 "", 130 "", 131 0, 132 0, 133 nil, 134 nil, 135 log, 136 )) 137 assert.True(t, finished, "operation should have finished successfully.") 138 } 139 140 func TestIsCurrentOperationFinished_ContentCheck_MarkedDeleted_NotFinished(t *testing.T) { 141 currentTid := "tid_1234" 142 testResponse := fmt.Sprintf(`{ "uuid" : "1234-1234", "publishReference" : "%s"}`, currentTid) 143 response := buildResponse(200, testResponse) 144 defer response.Body.Close() 145 contentCheck := &ContentCheck{ 146 mockHTTPCaller(t, "tid_pam_1234", response), 147 } 148 log := logger.NewUPPLogger("test", "PANIC") 149 150 pm := newPublishMetricBuilder().withTID(currentTid).withMarkedDeleted(true).build() 151 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 152 pm, 153 "", 154 "", 155 0, 156 0, 157 nil, 158 nil, 159 log, 160 )) 161 assert.False(t, finished, "operation should not have finished") 162 } 163 164 func TestIsCurrentOperationFinished_ContentCheck_LastModifiedDateIsAfterCurrentPublishDate_IgnoreCheckTrue(t *testing.T) { 165 currentTid := "tid_1234" 166 publishDate, err := time.Parse(DateLayout, "2016-01-08T14:22:06.271Z") 167 assert.Nil(t, err, "Failure in setting up test data") 168 169 testResponse := `{ "uuid" : "1234-1234", "publishReference" : "tid_1235", "lastModified" : "2016-01-08T14:22:07.391Z" }` 170 response := buildResponse(200, testResponse) 171 defer response.Body.Close() 172 contentCheck := &ContentCheck{ 173 mockHTTPCaller(t, "tid_pam_1234", response), 174 } 175 log := logger.NewUPPLogger("test", "PANIC") 176 177 pm := newPublishMetricBuilder().withTID(currentTid).withPublishDate(publishDate).build() 178 _, ignoreCheck := contentCheck.isCurrentOperationFinished(NewPublishCheck( 179 pm, 180 "", 181 "", 182 0, 183 0, 184 nil, 185 nil, 186 log, 187 )) 188 assert.True(t, ignoreCheck, "check should be ignored") 189 } 190 191 // fails for dateLayout="2006-01-02T15:04:05.000Z" 192 func TestIsCurrentOperationFinished_ContentCheck_LastModifiedDateIsBeforeCurrentPublishDateSpecifiedWith2Decimals_IgnoreCheckFalse(t *testing.T) { 193 currentTid := "tid_1234" 194 195 publishDate, err := time.Parse(DateLayout, "2016-02-01T14:30:21.55Z") 196 assert.Nil(t, err, "Failure in setting up test data") 197 198 testResponse := `{ "uuid" : "1234-1234", "publishReference" : "tid_1235", "lastModified" : "2016-02-01T14:30:21.549Z" }` 199 response := buildResponse(200, testResponse) 200 defer response.Body.Close() 201 contentCheck := &ContentCheck{ 202 mockHTTPCaller(t, "tid_pam_1234", response), 203 } 204 log := logger.NewUPPLogger("test", "PANIC") 205 206 pm := newPublishMetricBuilder().withTID(currentTid).withPublishDate(publishDate).build() 207 _, ignoreCheck := contentCheck.isCurrentOperationFinished(NewPublishCheck( 208 pm, 209 "", 210 "", 211 0, 212 0, 213 nil, 214 nil, 215 log, 216 )) 217 assert.False(t, ignoreCheck, "check should not be ignored") 218 } 219 220 func TestIsCurrentOperationFinished_ContentCheck_LastModifiedDateIsBeforeCurrentPublishDate_IgnoreCheckFalse(t *testing.T) { 221 currentTid := "tid_1234" 222 publishDate, err := time.Parse(DateLayout, "2016-01-08T14:22:06.271Z") 223 assert.Nil(t, err, "Failure in setting up test data") 224 225 testResponse := `{ "uuid" : "1234-1234", "publishReference" : "tid_1235", "lastModified" : "2016-01-08T14:22:05.391Z" }` 226 response := buildResponse(200, testResponse) 227 defer response.Body.Close() 228 contentCheck := &ContentCheck{ 229 mockHTTPCaller(t, "tid_pam_1234", response), 230 } 231 log := logger.NewUPPLogger("test", "PANIC") 232 233 pm := newPublishMetricBuilder().withTID(currentTid).withPublishDate(publishDate).build() 234 _, ignoreCheck := contentCheck.isCurrentOperationFinished(NewPublishCheck( 235 pm, 236 "", 237 "", 238 0, 239 0, 240 nil, 241 nil, 242 log, 243 )) 244 assert.False(t, ignoreCheck, "check should not be ignored") 245 } 246 247 func TestIsCurrentOperationFinished_ContentCheck_LastModifiedDateIsBeforeCurrentPublishDate_NotFinished(t *testing.T) { 248 currentTid := "tid_1234" 249 publishDate, err := time.Parse(DateLayout, "2016-01-08T14:22:06.271Z") 250 assert.Nil(t, err, "Failure in setting up test data") 251 252 testResponse := `{ "uuid" : "1234-1234", "publishReference" : "tid_1235", "lastModified" : "2016-01-08T14:22:05.391Z" }` 253 response := buildResponse(200, testResponse) 254 defer response.Body.Close() 255 contentCheck := &ContentCheck{ 256 mockHTTPCaller(t, "tid_pam_1234", response), 257 } 258 log := logger.NewUPPLogger("test", "PANIC") 259 260 pm := newPublishMetricBuilder().withTID(currentTid).withPublishDate(publishDate).build() 261 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 262 pm, 263 "", 264 "", 265 0, 266 0, 267 nil, 268 nil, 269 log, 270 )) 271 assert.False(t, finished, "operation should not have finished") 272 } 273 274 func TestIsCurrentOperationFinished_ContentCheck_LastModifiedDateEqualsCurrentPublishDate_Finished(t *testing.T) { 275 currentTid := "tid_1234" 276 publishDateAsString := "2016-01-08T14:22:06.271Z" 277 publishDate, err := time.Parse(DateLayout, publishDateAsString) 278 assert.Nil(t, err, "Failure in setting up test data") 279 280 testResponse := fmt.Sprintf(`{ "uuid" : "1234-1234", "publishReference" : "%s", "lastModified" : "%s" }`, currentTid, publishDateAsString) 281 response := buildResponse(200, testResponse) 282 defer response.Body.Close() 283 contentCheck := &ContentCheck{ 284 mockHTTPCaller(t, "tid_pam_1234", response), 285 } 286 log := logger.NewUPPLogger("test", "PANIC") 287 288 pm := newPublishMetricBuilder().withTID(currentTid).withPublishDate(publishDate).build() 289 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 290 pm, 291 "", 292 "", 293 0, 294 0, 295 nil, 296 nil, 297 log, 298 )) 299 assert.True(t, finished, "operation should have finished successfully") 300 } 301 302 // fallback to publish reference check if last modified date is not valid 303 func TestIsCurrentOperationFinished_ContentCheck_LastModifiedDateIsNullCurrentTIDAndPubReferenceMatch_Finished(t *testing.T) { 304 currentTid := "tid_1234" 305 publishDate, err := time.Parse(DateLayout, "2016-01-08T14:22:06.271Z") 306 assert.Nil(t, err, "Failure in setting up test data") 307 308 testResponse := fmt.Sprintf(`{ "uuid" : "1234-1234", "publishReference" : "%s", "lastModified" : null }`, currentTid) 309 response := buildResponse(200, testResponse) 310 defer response.Body.Close() 311 contentCheck := &ContentCheck{ 312 mockHTTPCaller(t, "tid_pam_1234", response), 313 } 314 log := logger.NewUPPLogger("test", "PANIC") 315 316 pm := newPublishMetricBuilder().withTID(currentTid).withPublishDate(publishDate).build() 317 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 318 pm, 319 "", 320 "", 321 0, 322 0, 323 nil, 324 nil, 325 log, 326 )) 327 assert.True(t, finished, "operation should have finished successfully") 328 } 329 330 // fallback to publish reference check if last modified date is not valid 331 func TestIsCurrentOperationFinished_ContentCheck_LastModifiedDateIsEmptyStringCurrentTIDAndPubReferenceMatch_Finished(t *testing.T) { 332 currentTid := "tid_1234" 333 publishDate, err := time.Parse(DateLayout, "2016-01-08T14:22:06.271Z") 334 assert.Nil(t, err, "Failure in setting up test data") 335 336 testResponse := fmt.Sprintf(`{ "uuid" : "1234-1234", "publishReference" : "%s", "lastModified" : "" }`, currentTid) 337 response := buildResponse(200, testResponse) 338 defer response.Body.Close() 339 contentCheck := &ContentCheck{ 340 mockHTTPCaller(t, "tid_pam_1234", response), 341 } 342 log := logger.NewUPPLogger("test", "PANIC") 343 344 pm := newPublishMetricBuilder().withTID(currentTid).withPublishDate(publishDate).build() 345 finished, _ := contentCheck.isCurrentOperationFinished(NewPublishCheck( 346 pm, 347 "", 348 "", 349 0, 350 0, 351 nil, 352 nil, 353 log, 354 )) 355 assert.True(t, finished, "operation should have finished successfully") 356 } 357 358 type publishMetricBuilder interface { 359 withUUID(string) publishMetricBuilder 360 withEndpoint(string) publishMetricBuilder 361 withPlatform(string) publishMetricBuilder 362 withTID(string) publishMetricBuilder 363 withMarkedDeleted(bool) publishMetricBuilder 364 withPublishDate(time.Time) publishMetricBuilder 365 build() metrics.PublishMetric 366 } 367 368 // PublishMetricBuilder implementation 369 type pmBuilder struct { 370 UUID string 371 endpoint url.URL 372 platform string 373 tid string 374 markedDeleted bool 375 publishDate time.Time 376 } 377 378 func (b *pmBuilder) withUUID(uuid string) publishMetricBuilder { 379 b.UUID = uuid 380 return b 381 } 382 383 func (b *pmBuilder) withEndpoint(endpoint string) publishMetricBuilder { 384 e, _ := url.Parse(endpoint) 385 b.endpoint = *e 386 return b 387 } 388 389 func (b *pmBuilder) withPlatform(platform string) publishMetricBuilder { 390 b.platform = platform 391 return b 392 } 393 394 func (b *pmBuilder) withTID(tid string) publishMetricBuilder { 395 b.tid = tid 396 return b 397 } 398 399 func (b *pmBuilder) withMarkedDeleted(markedDeleted bool) publishMetricBuilder { 400 b.markedDeleted = markedDeleted 401 return b 402 } 403 404 func (b *pmBuilder) withPublishDate(publishDate time.Time) publishMetricBuilder { 405 b.publishDate = publishDate 406 return b 407 } 408 409 func (b *pmBuilder) build() metrics.PublishMetric { 410 return metrics.PublishMetric{ 411 UUID: b.UUID, 412 Endpoint: b.endpoint, 413 Platform: b.platform, 414 TID: b.tid, 415 IsMarkedDeleted: b.markedDeleted, 416 PublishDate: b.publishDate, 417 } 418 } 419 420 func newPublishMetricBuilder() publishMetricBuilder { 421 return &pmBuilder{} 422 } 423 424 func buildResponse(statusCode int, content string) *http.Response { 425 return &http.Response{ 426 StatusCode: statusCode, 427 Body: io.NopCloser(bytes.NewBuffer([]byte(content))), 428 } 429 } 430 431 // mock httpCaller implementation 432 type testHTTPCaller struct { 433 t *testing.T 434 authUser string 435 authPass string 436 tid string 437 mockResponses []*http.Response 438 current int 439 } 440 441 // returns the mock responses of testHTTPCaller in order 442 func (t *testHTTPCaller) DoCall(config httpcaller.Config) (*http.Response, error) { 443 if t.authUser != config.Username || t.authPass != config.Password { 444 return buildResponse(401, `{message: "Not authenticated"}`), nil 445 } 446 447 if t.tid != "" { 448 assert.Equal(t.t, t.tid, config.TID, "transaction id") 449 } 450 451 response := t.mockResponses[t.current] 452 t.current = (t.current + 1) % len(t.mockResponses) 453 return response, nil 454 } 455 456 // builds testHTTPCaller with the given mocked responses in the provided order 457 func mockHTTPCaller(t *testing.T, tid string, responses ...*http.Response) httpcaller.Caller { 458 return &testHTTPCaller{t: t, tid: tid, mockResponses: responses} 459 } 460 461 // builds testHTTPCaller with the given mocked responses in the provided order 462 func mockAuthenticatedHTTPCaller(t *testing.T, tid string, username string, password string, responses ...*http.Response) httpcaller.Caller { 463 return &testHTTPCaller{t: t, tid: tid, authUser: username, authPass: password, mockResponses: responses} 464 }