...

Source file src/k8s.io/kube-openapi/pkg/handler/handler_test.go

Documentation: k8s.io/kube-openapi/pkg/handler

     1  package handler
     2  
     3  import (
     4  	json "encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"mime"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"os"
    11  	"reflect"
    12  	"testing"
    13  
    14  	"k8s.io/kube-openapi/pkg/cached"
    15  	"k8s.io/kube-openapi/pkg/validation/spec"
    16  )
    17  
    18  var returnedSwagger = []byte(`{
    19    "swagger": "2.0",
    20    "info": {
    21     "title": "Kubernetes",
    22     "version": "v1.11.0"
    23    }}`)
    24  
    25  func TestRegisterOpenAPIVersionedService(t *testing.T) {
    26  	var s spec.Swagger
    27  	err := s.UnmarshalJSON(returnedSwagger)
    28  	if err != nil {
    29  		t.Errorf("Unexpected error in unmarshalling SwaggerJSON: %v", err)
    30  	}
    31  
    32  	returnedJSON := normalizeSwaggerOrDie(returnedSwagger)
    33  	var decodedJSON map[string]interface{}
    34  	if err := json.Unmarshal(returnedJSON, &decodedJSON); err != nil {
    35  		t.Fatal(err)
    36  	}
    37  	returnedPb, err := ToProtoBinary(returnedJSON)
    38  	if err != nil {
    39  		t.Errorf("Unexpected error in preparing returnedPb: %v", err)
    40  	}
    41  
    42  	mux := http.NewServeMux()
    43  	o := NewOpenAPIService(&s)
    44  	o.RegisterOpenAPIVersionedService("/openapi/v2", mux)
    45  	server := httptest.NewServer(mux)
    46  	defer server.Close()
    47  	client := server.Client()
    48  
    49  	tcs := []struct {
    50  		acceptHeader              string
    51  		respStatus                int
    52  		responseContentTypeHeader string
    53  		respBody                  []byte
    54  	}{
    55  		{"", 200, "application/json", returnedJSON},
    56  		{"*/*", 200, "application/json", returnedJSON},
    57  		{"application/*", 200, "application/json", returnedJSON},
    58  		{"application/json", 200, "application/json", returnedJSON},
    59  		{"test/test", 406, "", []byte{}},
    60  		{"application/test", 406, "", []byte{}},
    61  		{"application/test, */*", 200, "application/json", returnedJSON},
    62  		{"application/test, application/json", 200, "application/json", returnedJSON},
    63  		{"application/com.github.proto-openapi.spec.v2.v1.0+protobuf", 200, "application/com.github.proto-openapi.spec.v2.v1.0+protobuf", returnedPb},
    64  		{"application/json, application/com.github.proto-openapi.spec.v2.v1.0+protobuf", 200, "application/json", returnedJSON},
    65  		{"application/com.github.proto-openapi.spec.v2.v1.0+protobuf, application/json", 200, "application/com.github.proto-openapi.spec.v2.v1.0+protobuf", returnedPb},
    66  		{"application/com.github.proto-openapi.spec.v2.v1.0+protobuf; q=0.5, application/json", 200, "application/json", returnedJSON},
    67  		{"application/com.github.proto-openapi.spec.v2@v1.0+protobuf", 200, "application/com.github.proto-openapi.spec.v2.v1.0+protobuf", returnedPb},
    68  		{"application/json, application/com.github.proto-openapi.spec.v2@v1.0+protobuf", 200, "application/json", returnedJSON},
    69  		{"application/com.github.proto-openapi.spec.v2@v1.0+protobuf, application/json", 200, "application/com.github.proto-openapi.spec.v2.v1.0+protobuf", returnedPb},
    70  		{"application/com.github.proto-openapi.spec.v2@v1.0+protobuf; q=0.5, application/json", 200, "application/json", returnedJSON},
    71  	}
    72  
    73  	for _, tc := range tcs {
    74  		req, err := http.NewRequest("GET", server.URL+"/openapi/v2", nil)
    75  		if err != nil {
    76  			t.Errorf("Accept: %v: Unexpected error in creating new request: %v", tc.acceptHeader, err)
    77  		}
    78  
    79  		req.Header.Add("Accept", tc.acceptHeader)
    80  		resp, err := client.Do(req)
    81  		if err != nil {
    82  			t.Errorf("Accept: %v: Unexpected error in serving HTTP request: %v", tc.acceptHeader, err)
    83  		}
    84  
    85  		if resp.StatusCode != tc.respStatus {
    86  			t.Errorf("Accept: %v: Unexpected response status code, want: %v, got: %v", tc.acceptHeader, tc.respStatus, resp.StatusCode)
    87  		}
    88  		if tc.respStatus != 200 {
    89  			continue
    90  		}
    91  
    92  		responseContentType := resp.Header.Get("Content-Type")
    93  		if responseContentType != tc.responseContentTypeHeader {
    94  			t.Errorf("Accept: %v: Unexpected content type in response, want: %v, got: %v", tc.acceptHeader, tc.responseContentTypeHeader, responseContentType)
    95  		}
    96  
    97  		_, _, err = mime.ParseMediaType(responseContentType)
    98  		if err != nil {
    99  			t.Errorf("Unexpected error in parsing response content type: %v, err: %v", responseContentType, err)
   100  		}
   101  
   102  		defer resp.Body.Close()
   103  		body, err := io.ReadAll(resp.Body)
   104  		if err != nil {
   105  			t.Errorf("Accept: %v: Unexpected error in reading response body: %v", tc.acceptHeader, err)
   106  		}
   107  		if !reflect.DeepEqual(body, tc.respBody) {
   108  			t.Errorf("Accept: %v: Response body mismatches, \nwant: %s, \ngot:  %s", tc.acceptHeader, string(tc.respBody), string(body))
   109  		}
   110  	}
   111  }
   112  
   113  var updatedSwagger = []byte(`{
   114    "swagger": "2.0",
   115    "info": {
   116     "title": "Kubernetes",
   117     "version": "v1.12.0"
   118    }}`)
   119  
   120  func getJSONBodyOrDie(server *httptest.Server) []byte {
   121  	return getBodyOrDie(server, "application/json")
   122  }
   123  
   124  func getProtoBodyOrDie(server *httptest.Server) []byte {
   125  	return getBodyOrDie(server, "application/com.github.proto-openapi.spec.v2.v1.0+protobuf")
   126  }
   127  
   128  func getBodyOrDie(server *httptest.Server, acceptHeader string) []byte {
   129  	req, err := http.NewRequest("GET", server.URL+"/openapi/v2", nil)
   130  	if err != nil {
   131  		panic(fmt.Errorf("Unexpected error in creating new request: %v", err))
   132  	}
   133  
   134  	req.Header.Add("Accept", acceptHeader)
   135  	resp, err := server.Client().Do(req)
   136  	if err != nil {
   137  		panic(fmt.Errorf("Unexpected error in serving HTTP request: %v", err))
   138  	}
   139  
   140  	defer resp.Body.Close()
   141  	body, err := io.ReadAll(resp.Body)
   142  	if err != nil {
   143  		panic(fmt.Errorf("Unexpected error in reading response body: %v", err))
   144  	}
   145  	return body
   146  }
   147  
   148  func normalizeSwaggerOrDie(j []byte) []byte {
   149  	var s spec.Swagger
   150  	err := s.UnmarshalJSON(j)
   151  	if err != nil {
   152  		panic(fmt.Errorf("Unexpected error in unmarshalling SwaggerJSON: %v", err))
   153  	}
   154  	rj, err := json.Marshal(s)
   155  	if err != nil {
   156  		panic(fmt.Errorf("Unexpected error in preparing returnedJSON: %v", err))
   157  	}
   158  	return rj
   159  }
   160  
   161  func TestUpdateSpecLazy(t *testing.T) {
   162  	returnedJSON := normalizeSwaggerOrDie(returnedSwagger)
   163  	var s spec.Swagger
   164  	err := s.UnmarshalJSON(returnedJSON)
   165  	if err != nil {
   166  		t.Errorf("Unexpected error in unmarshalling SwaggerJSON: %v", err)
   167  	}
   168  
   169  	mux := http.NewServeMux()
   170  	o := NewOpenAPIService(&s)
   171  	o.RegisterOpenAPIVersionedService("/openapi/v2", mux)
   172  	server := httptest.NewServer(mux)
   173  	defer server.Close()
   174  
   175  	body := string(getJSONBodyOrDie(server))
   176  	if body != string(returnedJSON) {
   177  		t.Errorf("Unexpected swagger received, got %q, expected %q", body, string(returnedSwagger))
   178  	}
   179  
   180  	o.UpdateSpecLazy(cached.Func(func() (*spec.Swagger, string, error) {
   181  		var s spec.Swagger
   182  		err := s.UnmarshalJSON(updatedSwagger)
   183  		if err != nil {
   184  			t.Errorf("Unexpected error in unmarshalling SwaggerJSON: %v", err)
   185  		}
   186  		return &s, "SOMEHASH", nil
   187  	}))
   188  
   189  	updatedJSON := normalizeSwaggerOrDie(updatedSwagger)
   190  	body = string(getJSONBodyOrDie(server))
   191  
   192  	if body != string(updatedJSON) {
   193  		t.Errorf("Unexpected swagger received, got %q, expected %q", body, string(updatedJSON))
   194  	}
   195  }
   196  
   197  func TestToProtoBinary(t *testing.T) {
   198  	bs, err := os.ReadFile("../../test/integration/testdata/aggregator/openapi.json")
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	if _, err := ToProtoBinary(bs); err != nil {
   203  		t.Fatal()
   204  	}
   205  	// TODO: add some kind of roundtrip test here
   206  }
   207  
   208  func TestConcurrentReadStaleCache(t *testing.T) {
   209  	// Number of requests sent in parallel
   210  	concurrency := 5
   211  
   212  	var s spec.Swagger
   213  	err := s.UnmarshalJSON(returnedSwagger)
   214  	if err != nil {
   215  		t.Errorf("Unexpected error in unmarshalling SwaggerJSON: %v", err)
   216  	}
   217  
   218  	mux := http.NewServeMux()
   219  	o := NewOpenAPIService(&s)
   220  	o.RegisterOpenAPIVersionedService("/openapi/v2", mux)
   221  	server := httptest.NewServer(mux)
   222  	defer server.Close()
   223  
   224  	returnedJSON := normalizeSwaggerOrDie(returnedSwagger)
   225  	returnedPb, err := ToProtoBinary(returnedJSON)
   226  	if err != nil {
   227  		t.Errorf("Unexpected error in preparing returnedPb: %v", err)
   228  	}
   229  
   230  	jsonResultsChan := make(chan []byte)
   231  	protoResultsChan := make(chan []byte)
   232  	updateSpecChan := make(chan struct{})
   233  	for i := 0; i < concurrency; i++ {
   234  		go func() {
   235  			sc := s
   236  			o.UpdateSpec(&sc)
   237  			updateSpecChan <- struct{}{}
   238  		}()
   239  		go func() { jsonResultsChan <- getJSONBodyOrDie(server) }()
   240  		go func() { protoResultsChan <- getProtoBodyOrDie(server) }()
   241  	}
   242  	for i := 0; i < concurrency; i++ {
   243  		r := <-jsonResultsChan
   244  		if !reflect.DeepEqual(r, returnedJSON) {
   245  			t.Errorf("Returned and expected JSON do not match: got %v, want %v", string(r), string(returnedJSON))
   246  		}
   247  	}
   248  	for i := 0; i < concurrency; i++ {
   249  		r := <-protoResultsChan
   250  		if !reflect.DeepEqual(r, returnedPb) {
   251  			t.Errorf("Returned and expected pb do not match: got %v, want %v", r, returnedPb)
   252  		}
   253  	}
   254  	for i := 0; i < concurrency; i++ {
   255  		<-updateSpecChan
   256  	}
   257  }
   258  

View as plain text