1// Copyright 2017 Microsoft Corporation
2//
3//  Licensed under the Apache License, Version 2.0 (the "License");
4//  you may not use this file except in compliance with the License.
5//  You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14
15package azure
16
17import (
18	"context"
19	"net/http"
20	"sync"
21	"testing"
22	"time"
23
24	"github.com/Azure/go-autorest/autorest"
25	"github.com/Azure/go-autorest/autorest/mocks"
26)
27
28func TestDoRetryWithRegistration(t *testing.T) {
29	client := mocks.NewSender()
30	// first response, should retry because it is a transient error
31	client.AppendResponse(mocks.NewResponseWithStatus("Internal server error", http.StatusInternalServerError))
32	// response indicates the resource provider has not been registered
33	client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{
34	"error":{
35		"code":"MissingSubscriptionRegistration",
36		"message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions.",
37		"details":[
38			{
39				"code":"MissingSubscriptionRegistration",
40				"target":"Microsoft.EventGrid",
41				"message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions."
42			}
43		]
44	}
45}
46`), http.StatusConflict, "MissingSubscriptionRegistration"))
47	// first poll response, still not ready
48	client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{
49	"registrationState": "Registering"
50}
51`), http.StatusOK, "200 OK"))
52	// last poll response, respurce provider has been registered
53	client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{
54	"registrationState": "Registered"
55}
56`), http.StatusOK, "200 OK"))
57	// retry original request, response is successful
58	client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK))
59
60	req := mocks.NewRequestForURL("https://lol/subscriptions/rofl")
61	req.Body = mocks.NewBody("lolol")
62	r, err := autorest.SendWithSender(client, req,
63		DoRetryWithRegistration(autorest.Client{
64			PollingDelay:    time.Second,
65			PollingDuration: time.Second * 10,
66			RetryAttempts:   5,
67			RetryDuration:   time.Second,
68			Sender:          client,
69		}),
70	)
71	if err != nil {
72		t.Fatalf("got error: %v", err)
73	}
74
75	autorest.Respond(r,
76		autorest.ByDiscardingBody(),
77		autorest.ByClosing(),
78	)
79
80	if r.StatusCode != http.StatusOK {
81		t.Fatalf("azure: Sender#DoRetryWithRegistration -- Got: StatusCode %v; Want: StatusCode 200 OK", r.StatusCode)
82	}
83}
84
85func TestDoRetrySkipRegistration(t *testing.T) {
86	client := mocks.NewSender()
87	// first response, should retry because it is a transient error
88	client.AppendResponse(mocks.NewResponseWithStatus("Internal server error", http.StatusInternalServerError))
89	// response indicates the resource provider has not been registered
90	client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{
91	"error":{
92		"code":"MissingSubscriptionRegistration",
93		"message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions.",
94		"details":[
95			{
96				"code":"MissingSubscriptionRegistration",
97				"target":"Microsoft.EventGrid",
98				"message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions."
99			}
100		]
101	}
102}`), http.StatusConflict, "MissingSubscriptionRegistration"))
103
104	req := mocks.NewRequestForURL("https://lol/subscriptions/rofl")
105	req.Body = mocks.NewBody("lolol")
106	r, err := autorest.SendWithSender(client, req,
107		DoRetryWithRegistration(autorest.Client{
108			PollingDelay:                     time.Second,
109			PollingDuration:                  time.Second * 10,
110			RetryAttempts:                    5,
111			RetryDuration:                    time.Second,
112			Sender:                           client,
113			SkipResourceProviderRegistration: true,
114		}),
115	)
116	if err != nil {
117		t.Fatalf("got error: %v", err)
118	}
119
120	autorest.Respond(r,
121		autorest.ByDiscardingBody(),
122		autorest.ByClosing(),
123	)
124
125	if r.StatusCode != http.StatusConflict {
126		t.Fatalf("azure: Sender#DoRetryWithRegistration -- Got: StatusCode %v; Want: StatusCode 409 Conflict", r.StatusCode)
127	}
128}
129
130func TestDoRetryWithRegistration_CanBeCancelled(t *testing.T) {
131	ctx, cancel := context.WithCancel(context.Background())
132	delay := 5 * time.Second
133
134	client := mocks.NewSender()
135	client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("Internal server error", http.StatusInternalServerError), 5)
136
137	var wg sync.WaitGroup
138	wg.Add(1)
139	start := time.Now()
140	end := time.Now()
141	var err error
142
143	go func() {
144		req := mocks.NewRequestForURL("https://lol/subscriptions/rofl")
145		req = req.WithContext(ctx)
146		req.Body = mocks.NewBody("lolol")
147		_, err = autorest.SendWithSender(client, req,
148			DoRetryWithRegistration(autorest.Client{
149				PollingDelay:                     time.Second,
150				PollingDuration:                  delay,
151				RetryAttempts:                    5,
152				RetryDuration:                    time.Second,
153				Sender:                           client,
154				SkipResourceProviderRegistration: true,
155			}),
156		)
157		end = time.Now()
158		wg.Done()
159	}()
160	cancel()
161	wg.Wait()
162	time.Sleep(5 * time.Millisecond)
163	if err == nil {
164		t.Fatalf("azure: DoRetryWithRegistration didn't cancel")
165	}
166	if end.Sub(start) >= delay {
167		t.Fatalf("azure: DoRetryWithRegistration failed to cancel")
168	}
169}
170