11package hostgacommunicator
22
33import (
4+ "bytes"
5+ "io"
6+ "net/http"
47 "os"
58 "testing"
69
10+ "github.com/Azure/run-command-handler-linux/internal/constants"
11+ "github.com/Azure/run-command-handler-linux/internal/requesthelper"
712 "github.com/go-kit/kit/log"
13+ "github.com/pkg/errors"
814 "github.com/stretchr/testify/require"
915)
1016
@@ -16,3 +22,159 @@ func Test_GetOperationUri(t *testing.T) {
1622 require .NotNil (t , uri )
1723 require .Contains (t , uri , operationName )
1824}
25+
26+ type fakeVMSettingsRequestManager struct {
27+ rm * requesthelper.RequestManager
28+ err error
29+ }
30+
31+ func (f fakeVMSettingsRequestManager ) GetVMSettingsRequestManager (ctx * log.Context ) (* requesthelper.RequestManager , error ) {
32+ return f .rm , f .err
33+ }
34+
35+ func TestGetImmediateVMSettings_RequestManagerError (t * testing.T ) {
36+ orig := withRetriesFn
37+ t .Cleanup (func () { withRetriesFn = orig })
38+
39+ // withRetries should never be called in this branch
40+ withRetriesFn = func (_ * log.Context , _ * requesthelper.RequestManager , _ requesthelper.SleepFunc , _ string ) (* http.Response , error ) {
41+ t .Fatalf ("withRetriesFn should not have been called" )
42+ return nil , nil
43+ }
44+
45+ rmErr := errors .New ("the chipmunks have new management" )
46+ c := NewHostGACommunicator (fakeVMSettingsRequestManager {rm : nil , err : rmErr })
47+
48+ _ , err := c .GetImmediateVMSettings (nil , "etag0" )
49+ VerifyErrorClarification (t , constants .Internal_UnknownError , err )
50+ }
51+
52+ func TestGetImmediateVMSettings_WithRetriesError_WrappedWithClarification (t * testing.T ) {
53+ orig := withRetriesFn
54+ t .Cleanup (func () { withRetriesFn = orig })
55+
56+ withRetriesFn = func (_ * log.Context , _ * requesthelper.RequestManager , _ requesthelper.SleepFunc , _ string ) (* http.Response , error ) {
57+ return nil , errors .New ("network fail" )
58+ }
59+
60+ c := NewHostGACommunicator (fakeVMSettingsRequestManager {rm : & requesthelper.RequestManager {}, err : nil })
61+
62+ _ , err := c .GetImmediateVMSettings (nil , "etag0" )
63+ VerifyErrorClarification (t , constants .Internal_UnknownError , err )
64+ }
65+
66+ func TestGetImmediateVMSettings_NotModified304_ReturnsUnmodifiedResponse (t * testing.T ) {
67+ orig := withRetriesFn
68+ t .Cleanup (func () { withRetriesFn = orig })
69+
70+ withRetriesFn = func (_ * log.Context , _ * requesthelper.RequestManager , _ requesthelper.SleepFunc , _ string ) (* http.Response , error ) {
71+ return & http.Response {
72+ StatusCode : http .StatusNotModified ,
73+ Body : io .NopCloser (bytes .NewReader (nil )),
74+ Header : make (http.Header ),
75+ }, nil
76+ }
77+
78+ c := NewHostGACommunicator (fakeVMSettingsRequestManager {rm : & requesthelper.RequestManager {}, err : nil })
79+
80+ resp , err := c .GetImmediateVMSettings (nil , "etag0" )
81+ require .Nil (t , err , "unexpected err: %v" , err )
82+ require .Nil (t , resp .VMSettings , "expected VMSettings nil" )
83+ require .Equal (t , "etag0" , resp .ETag , "expected ETag preserved, got %q" , resp .ETag )
84+ require .False (t , resp .Modified , "expected Modified=false" )
85+ }
86+
87+ func TestGetImmediateVMSettings_NotFound404_ReturnsUnmodifiedResponse (t * testing.T ) {
88+ orig := withRetriesFn
89+ t .Cleanup (func () { withRetriesFn = orig })
90+
91+ withRetriesFn = func (_ * log.Context , _ * requesthelper.RequestManager , _ requesthelper.SleepFunc , _ string ) (* http.Response , error ) {
92+ return & http.Response {
93+ StatusCode : http .StatusNotFound ,
94+ Body : io .NopCloser (bytes .NewReader (nil )),
95+ Header : make (http.Header ),
96+ }, nil
97+ }
98+
99+ c := NewHostGACommunicator (fakeVMSettingsRequestManager {rm : & requesthelper.RequestManager {}, err : nil })
100+
101+ resp , err := c .GetImmediateVMSettings (nil , "etag0" )
102+ require .Nil (t , err , "unexpected err: %v" , err )
103+ require .Nil (t , resp .VMSettings , "expected VMSettings nil" )
104+ require .Equal (t , "etag0" , resp .ETag , "expected ETag preserved, got %q" , resp .ETag )
105+ require .False (t , resp .Modified , "expected Modified=false" )
106+ }
107+
108+ func TestGetImmediateVMSettings_BadJSON_ReturnsFailedToParseSettings (t * testing.T ) {
109+ orig := withRetriesFn
110+ t .Cleanup (func () { withRetriesFn = orig })
111+
112+ withRetriesFn = func (_ * log.Context , _ * requesthelper.RequestManager , _ requesthelper.SleepFunc , _ string ) (* http.Response , error ) {
113+ h := make (http.Header )
114+ h .Set (constants .ETagHeaderName , "etag1" ) // still present, but parse should fail first
115+ return & http.Response {
116+ StatusCode : http .StatusOK ,
117+ Body : io .NopCloser (bytes .NewReader ([]byte ("{not-json" ))),
118+ Header : h ,
119+ }, nil
120+ }
121+
122+ c := NewHostGACommunicator (fakeVMSettingsRequestManager {rm : & requesthelper.RequestManager {}, err : nil })
123+
124+ _ , err := c .GetImmediateVMSettings (nil , "etag0" )
125+ VerifyErrorClarification (t , constants .Hgap_FailedToParseImmediateSettings , err )
126+ }
127+
128+ func TestGetImmediateVMSettings_MissingETagHeader_ReturnsEtagNotFoundClarification (t * testing.T ) {
129+ orig := withRetriesFn
130+ t .Cleanup (func () { withRetriesFn = orig })
131+
132+ withRetriesFn = func (_ * log.Context , _ * requesthelper.RequestManager , _ requesthelper.SleepFunc , _ string ) (* http.Response , error ) {
133+ // minimal valid JSON for VMImmediateExtensionsGoalState; if required fields exist, update accordingly.
134+ body := []byte (`{}` )
135+ return & http.Response {
136+ StatusCode : http .StatusOK ,
137+ Body : io .NopCloser (bytes .NewReader (body )),
138+ Header : make (http.Header ), // no ETag set
139+ }, nil
140+ }
141+
142+ c := NewHostGACommunicator (fakeVMSettingsRequestManager {rm : & requesthelper.RequestManager {}, err : nil })
143+
144+ _ , err := c .GetImmediateVMSettings (nil , "etag0" )
145+ VerifyErrorClarification (t , constants .Hgap_EtagNotFound , err )
146+ }
147+
148+ func TestGetImmediateVMSettings_Success_ModifiedFlagAndETagReturned (t * testing.T ) {
149+ orig := withRetriesFn
150+ t .Cleanup (func () { withRetriesFn = orig })
151+
152+ withRetriesFn = func (_ * log.Context , _ * requesthelper.RequestManager , _ requesthelper.SleepFunc , _ string ) (* http.Response , error ) {
153+ h := make (http.Header )
154+ h .Set (constants .ETagHeaderName , "etag1" )
155+ return & http.Response {
156+ StatusCode : http .StatusOK ,
157+ Body : io .NopCloser (bytes .NewReader ([]byte (`{}` ))),
158+ Header : h ,
159+ }, nil
160+ }
161+
162+ c := NewHostGACommunicator (fakeVMSettingsRequestManager {rm : & requesthelper.RequestManager {}, err : nil })
163+
164+ resp , err := c .GetImmediateVMSettings (nil , "etag0" )
165+ require .Nil (t , err , "unexpected err: %v" , err )
166+ require .NotNil (t , resp .VMSettings , "expected VMSettings non-nil" )
167+ require .Equal (t , "etag1" , resp .ETag , "expected etag1 preserved, got %q" , resp .ETag )
168+ require .True (t , resp .Modified , "expected Modified=true when etag changes" )
169+ }
170+
171+ func TestGetOperationUri_InvalidFallbackAddress (t * testing.T ) {
172+ orig := WireServerFallbackAddress
173+ t .Cleanup (func () { WireServerFallbackAddress = orig })
174+
175+ // This should make url.Parse fail (unclosed IPv6 literal).
176+ WireServerFallbackAddress = "http://[::1:32526"
177+
178+ _ , err := getOperationUri (nil , "/machine" )
179+ VerifyErrorClarification (t , constants .Hgap_FailedToParseAddress , err )
180+ }
0 commit comments