github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/cps/history_test.go (about)

     1  package cps
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func TestGetDVHistory(t *testing.T) {
    15  	tests := map[string]struct {
    16  		request          GetDVHistoryRequest
    17  		responseBody     string
    18  		responseStatus   int
    19  		withError        error
    20  		expectedPath     string
    21  		expectedResponse *GetDVHistoryResponse
    22  		expectedHeaders  map[string]string
    23  	}{
    24  		"200 ok": {
    25  			request: GetDVHistoryRequest{EnrollmentID: 28926},
    26  			responseBody: `
    27  {
    28     "results": [
    29         {
    30             "domain": "bartdtest.sqa-il.com",
    31             "domainHistory": [
    32                 {
    33                     "domain": "bartdtest.sqa-il.com",
    34                     "responseBody": "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs.xI50JalgaT8I71x4FcarzkEx1UemXcsDqndvWaQqDgc",
    35                     "fullPath": "http://bartdtest.sqa-il.com/.well-known/acme-challenge/zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
    36                     "token": "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
    37                     "status": "Error",
    38                     "error": "Expired authorization",
    39                     "validationStatus": "EXPIRED",
    40                     "requestTimestamp": "2022-05-18T20:12:05Z",
    41                     "validatedTimestamp": "2022-05-25T20:14:05Z",
    42                     "expires": "2022-05-25T20:12:05Z",
    43                     "redirectFullPath": "http://dcv.akamai.com/.well-known/acme-challenge/zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
    44                     "validationRecords": [],
    45                     "challenges": [
    46                         {
    47                             "type": "http-01",
    48                             "status": "pending",
    49                             "error": null,
    50                             "token": "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
    51                             "responseBody": "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs.xI50JalgaT8I71x4FcarzkEx1UemXcsDqndvWaQqDgc",
    52                             "fullPath": "http://bartdtest.sqa-il.com/.well-known/acme-challenge/zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
    53                             "redirectFullPath": "http://dcv.akamai.com/.well-known/acme-challenge/zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
    54                             "validationRecords": []
    55                         },
    56                         {
    57                             "type": "dns-01",
    58                             "status": "pending",
    59                             "error": null,
    60                             "token": "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
    61                             "responseBody": "qyxqdksaqsLTbWIt0im_ob0wYRUVH_Nfe91rmTD3bn0",
    62                             "fullPath": "_acme-challenge.bartdtest.sqa-il.com.",
    63                             "redirectFullPath": "",
    64                             "validationRecords": []
    65                         }
    66                     ]
    67                 },
    68                 {
    69                     "domain": "bartdtest.sqa-il.com",
    70                     "responseBody": "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI.QzVNu9F4w2DPQOMaqyiwLtnih04pcfunDeZx-LK3h24",
    71                     "fullPath": "http://bartdtest.sqa-il.com/.well-known/acme-challenge/7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
    72                     "token": "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
    73                     "status": "Error",
    74                     "error": "Expired authorization",
    75                     "validationStatus": "EXPIRED",
    76                     "requestTimestamp": "2022-05-25T20:14:35Z",
    77                     "validatedTimestamp": "2022-06-01T20:15:13Z",
    78                     "expires": "2022-06-01T20:14:35Z",
    79                     "redirectFullPath": "http://dcv.akamai.com/.well-known/acme-challenge/7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
    80                     "validationRecords": [],
    81                     "challenges": [
    82                         {
    83                             "type": "http-01",
    84                             "status": "pending",
    85                             "error": null,
    86                             "token": "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
    87                             "responseBody": "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI.QzVNu9F4w2DPQOMaqyiwLtnih04pcfunDeZx-LK3h24",
    88                             "fullPath": "http://bartdtest.sqa-il.com/.well-known/acme-challenge/7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
    89                             "redirectFullPath": "http://dcv.akamai.com/.well-known/acme-challenge/7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
    90                             "validationRecords": []
    91                         },
    92                         {
    93                             "type": "dns-01",
    94                             "status": "pending",
    95                             "error": null,
    96                             "token": "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
    97                             "responseBody": "fNnU1Y9bCKG1jET8px-yE5cSd9HXMg-n6N1rCL0BqdE",
    98                             "fullPath": "_acme-challenge.bartdtest.sqa-il.com.",
    99                             "redirectFullPath": "",
   100                             "validationRecords": []
   101                         }
   102                     ]
   103                 }
   104             ]
   105         }
   106     ]
   107  }
   108  `,
   109  			responseStatus: http.StatusOK,
   110  			expectedPath:   "/cps/v2/enrollments/28926/dv-history",
   111  			expectedResponse: &GetDVHistoryResponse{Results: []HistoryResult{{
   112  				Domain: "bartdtest.sqa-il.com",
   113  				DomainHistory: []DomainHistory{
   114  					{
   115  						Domain:             "bartdtest.sqa-il.com",
   116  						ResponseBody:       "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs.xI50JalgaT8I71x4FcarzkEx1UemXcsDqndvWaQqDgc",
   117  						FullPath:           "http://bartdtest.sqa-il.com/.well-known/acme-challenge/zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
   118  						Token:              "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
   119  						Status:             "Error",
   120  						Error:              "Expired authorization",
   121  						ValidationRecords:  []ValidationRecord{},
   122  						ValidationStatus:   "EXPIRED",
   123  						RequestTimestamp:   "2022-05-18T20:12:05Z",
   124  						ValidatedTimestamp: "2022-05-25T20:14:05Z",
   125  						Expires:            "2022-05-25T20:12:05Z",
   126  						RedirectFullPath:   "http://dcv.akamai.com/.well-known/acme-challenge/zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
   127  						Challenges: []Challenge{
   128  							{
   129  								Type:              "http-01",
   130  								Status:            "pending",
   131  								Token:             "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
   132  								ResponseBody:      "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs.xI50JalgaT8I71x4FcarzkEx1UemXcsDqndvWaQqDgc",
   133  								FullPath:          "http://bartdtest.sqa-il.com/.well-known/acme-challenge/zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
   134  								RedirectFullPath:  "http://dcv.akamai.com/.well-known/acme-challenge/zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
   135  								ValidationRecords: []ValidationRecord{},
   136  							},
   137  							{
   138  								Type:              "dns-01",
   139  								Status:            "pending",
   140  								Token:             "zVylMi1AXeE6XbLprYQayt3iojtMSuhELHwhJ2t2pfs",
   141  								ResponseBody:      "qyxqdksaqsLTbWIt0im_ob0wYRUVH_Nfe91rmTD3bn0",
   142  								FullPath:          "_acme-challenge.bartdtest.sqa-il.com.",
   143  								RedirectFullPath:  "",
   144  								ValidationRecords: []ValidationRecord{},
   145  							},
   146  						},
   147  					},
   148  					{
   149  						Domain:             "bartdtest.sqa-il.com",
   150  						ResponseBody:       "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI.QzVNu9F4w2DPQOMaqyiwLtnih04pcfunDeZx-LK3h24",
   151  						FullPath:           "http://bartdtest.sqa-il.com/.well-known/acme-challenge/7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
   152  						Token:              "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
   153  						Status:             "Error",
   154  						Error:              "Expired authorization",
   155  						ValidationRecords:  []ValidationRecord{},
   156  						ValidationStatus:   "EXPIRED",
   157  						RequestTimestamp:   "2022-05-25T20:14:35Z",
   158  						ValidatedTimestamp: "2022-06-01T20:15:13Z",
   159  						Expires:            "2022-06-01T20:14:35Z",
   160  						RedirectFullPath:   "http://dcv.akamai.com/.well-known/acme-challenge/7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
   161  						Challenges: []Challenge{
   162  							{
   163  								Type:              "http-01",
   164  								Status:            "pending",
   165  								Token:             "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
   166  								ResponseBody:      "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI.QzVNu9F4w2DPQOMaqyiwLtnih04pcfunDeZx-LK3h24",
   167  								FullPath:          "http://bartdtest.sqa-il.com/.well-known/acme-challenge/7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
   168  								RedirectFullPath:  "http://dcv.akamai.com/.well-known/acme-challenge/7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
   169  								ValidationRecords: []ValidationRecord{},
   170  							},
   171  							{
   172  								Type:              "dns-01",
   173  								Status:            "pending",
   174  								Token:             "7LwF89FciEFJZb_CUO0xogHQEh-r2iwN6R4BNHvLSoI",
   175  								ResponseBody:      "fNnU1Y9bCKG1jET8px-yE5cSd9HXMg-n6N1rCL0BqdE",
   176  								FullPath:          "_acme-challenge.bartdtest.sqa-il.com.",
   177  								RedirectFullPath:  "",
   178  								ValidationRecords: []ValidationRecord{},
   179  							},
   180  						},
   181  					},
   182  				},
   183  			}}},
   184  			expectedHeaders: map[string]string{
   185  				"Accept": "application/vnd.akamai.cps.dv-history.v1+json",
   186  			},
   187  		},
   188  		"404 not found": {
   189  			request: GetDVHistoryRequest{EnrollmentID: 28926000},
   190  			responseBody: `
   191  {
   192      "type": "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found",
   193      "title": "Not Found",
   194      "instance": "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found?id=e925fbec2ef84012a4bee2782c5b0715"
   195  }
   196  `,
   197  			responseStatus: http.StatusNotFound,
   198  			withError: &Error{
   199  				Type:       "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found",
   200  				Title:      "Not Found",
   201  				Instance:   "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found?id=e925fbec2ef84012a4bee2782c5b0715",
   202  				StatusCode: 404,
   203  			},
   204  			expectedPath: "/cps/v2/enrollments/28926000/dv-history",
   205  		},
   206  		"missing enrollmentID": {
   207  			request:   GetDVHistoryRequest{},
   208  			withError: ErrStructValidation,
   209  		},
   210  	}
   211  
   212  	for name, test := range tests {
   213  		t.Run(name, func(t *testing.T) {
   214  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   215  				assert.Equal(t, test.expectedPath, r.URL.String())
   216  				assert.Equal(t, http.MethodGet, r.Method)
   217  				for k, v := range test.expectedHeaders {
   218  					assert.Equal(t, v, r.Header.Get(k))
   219  				}
   220  				w.WriteHeader(test.responseStatus)
   221  				_, err := w.Write([]byte(test.responseBody))
   222  				assert.NoError(t, err)
   223  			}))
   224  			client := mockAPIClient(t, mockServer)
   225  			result, err := client.GetDVHistory(context.Background(), test.request)
   226  			if test.withError != nil {
   227  				assert.True(t, errors.Is(err, test.withError), "want: %s; got: %s", test.withError, err)
   228  				return
   229  			}
   230  			require.NoError(t, err)
   231  			assert.Equal(t, test.expectedResponse, result)
   232  		})
   233  	}
   234  }
   235  
   236  func TestGetCertificateHistory(t *testing.T) {
   237  	tests := map[string]struct {
   238  		request          GetCertificateHistoryRequest
   239  		responseBody     string
   240  		responseStatus   int
   241  		withError        error
   242  		expectedPath     string
   243  		expectedResponse *GetCertificateHistoryResponse
   244  		expectedHeaders  map[string]string
   245  	}{
   246  		"200 ok": {
   247  			request: GetCertificateHistoryRequest{EnrollmentID: 28926},
   248  			responseBody: `
   249  {
   250      "certificates": [
   251          {
   252              "type": "third-party",
   253              "deploymentStatus": "active",
   254              "stagingStatus": "active",
   255              "slots": [
   256                  757836
   257              ],
   258              "geography": "core",
   259              "ra": "third-party",
   260              "primaryCertificate": {
   261                  "certificate": "-----BEGIN CERTIFICATE-----\nMIIDpDCCAgygAwIBAgIQG7UFcE+swJPyIGzHEPrWTzANBgkqhkiG9w0BAQsFADCB\njTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTEwLwYDVQQLDCh3emFn\ncmFqY0BrcmstbXAwNmMgKFdvamNpZWNoIFphZ3JhamN6dWspMTgwNgYDVQQDDC9t\na2NlcnQgd3phZ3JhamNAa3JrLW1wMDZjIChXb2pjaWVjaCBaYWdyYWpjenVrKTAe\nFw0yMjA3MjAwNjE0MjlaFw0yNDEwMjAwNjE0MjlaMGsxCzAJBgNVBAYTAlBMMQ0w\nCwYDVQQIEwR0ZXN0MQ0wCwYDVQQHEwR0ZXN0MQ0wCwYDVQQKEwR0ZXN0MQ0wCwYD\nVQQLEwR0ZXN0MSAwHgYDVQQDExdjcHMud3phLXRlc3QwMDEuYWthdGVzdDBZMBMG\nByqGSM49AgEGCCqGSM49AwEHA0IABLAKxSxwaZgqpFVHhFi6feDQ6A8Q0S71p/mP\nuwkUD1zNvmyKPzDflAWDGTyocC8aCzGCHiFdt6CRhCy25RwDK2mjbDBqMA4GA1Ud\nDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBTpiXDW\nLsgNHer9dSJuuzAt6LBpWzAiBgNVHREEGzAZghdjcHMud3phLXRlc3QwMDEuYWth\ndGVzdDANBgkqhkiG9w0BAQsFAAOCAYEAiXmq64svercHBIFbHfNPrRns3ccrOy0U\n+zHks6uSGEO2S/wnAhDtpK2D103SnddRK4WXDF3w0F/GCqrXGmWbKQvzukaUTBbl\neO4Wy36qwc/SypvjzZsPI2q2E6e7uONcB2cL6UA18aEO/w9X4jTfWS5zMV1zPO3N\naozEFRXrziFYGrPAJ9o2RldScHU9stl0TcIiuDWzUqPxvseGBuEMFNtVko590cJA\nl36muto6uC0XV8RtEaMAfbHe1yC64AJd+DzQP47ORQSR3L2+jA8oDQ9pJ+meiBA6\nAt6AJpI1MsekXleRHnZxacUMVYzdk4c472xst+0ueHyMdaIbWgii54csrDfy2vPw\nZwDNm437wJQvqg4RcUrOd5IoM37UCDfyisU9csY4yMXFxwwKFQtIk4Bn+lGbWdjC\nF+NSS+ujtHl0d5rg12QXegbWtFIol+E/ntxG4uS97dpldO2+cQCMM8RGsXA75teL\n4+HRwu0IEa7aaZZAVDZUA2U6wtAmhFM3\n-----END CERTIFICATE-----",
   262                  "trustChain": "",
   263                  "expiry": "2024-10-20T06:14:29Z",
   264                  "keyAlgorithm": "ECDSA"
   265              },
   266              "multiStackedCertificates": []
   267          }
   268      ]
   269  }
   270  `,
   271  			responseStatus: http.StatusOK,
   272  			expectedPath:   "/cps/v2/enrollments/28926/history/certificates",
   273  			expectedResponse: &GetCertificateHistoryResponse{
   274  				Certificates: []HistoryCertificate{
   275  					{
   276  						Type:             "third-party",
   277  						DeploymentStatus: "active",
   278  						StagingStatus:    "active",
   279  						Slots:            []int{757836},
   280  						Geography:        "core",
   281  						RA:               "third-party",
   282  						PrimaryCertificate: CertificateObject{
   283  							Certificate:  "-----BEGIN CERTIFICATE-----\nMIIDpDCCAgygAwIBAgIQG7UFcE+swJPyIGzHEPrWTzANBgkqhkiG9w0BAQsFADCB\njTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTEwLwYDVQQLDCh3emFn\ncmFqY0BrcmstbXAwNmMgKFdvamNpZWNoIFphZ3JhamN6dWspMTgwNgYDVQQDDC9t\na2NlcnQgd3phZ3JhamNAa3JrLW1wMDZjIChXb2pjaWVjaCBaYWdyYWpjenVrKTAe\nFw0yMjA3MjAwNjE0MjlaFw0yNDEwMjAwNjE0MjlaMGsxCzAJBgNVBAYTAlBMMQ0w\nCwYDVQQIEwR0ZXN0MQ0wCwYDVQQHEwR0ZXN0MQ0wCwYDVQQKEwR0ZXN0MQ0wCwYD\nVQQLEwR0ZXN0MSAwHgYDVQQDExdjcHMud3phLXRlc3QwMDEuYWthdGVzdDBZMBMG\nByqGSM49AgEGCCqGSM49AwEHA0IABLAKxSxwaZgqpFVHhFi6feDQ6A8Q0S71p/mP\nuwkUD1zNvmyKPzDflAWDGTyocC8aCzGCHiFdt6CRhCy25RwDK2mjbDBqMA4GA1Ud\nDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBTpiXDW\nLsgNHer9dSJuuzAt6LBpWzAiBgNVHREEGzAZghdjcHMud3phLXRlc3QwMDEuYWth\ndGVzdDANBgkqhkiG9w0BAQsFAAOCAYEAiXmq64svercHBIFbHfNPrRns3ccrOy0U\n+zHks6uSGEO2S/wnAhDtpK2D103SnddRK4WXDF3w0F/GCqrXGmWbKQvzukaUTBbl\neO4Wy36qwc/SypvjzZsPI2q2E6e7uONcB2cL6UA18aEO/w9X4jTfWS5zMV1zPO3N\naozEFRXrziFYGrPAJ9o2RldScHU9stl0TcIiuDWzUqPxvseGBuEMFNtVko590cJA\nl36muto6uC0XV8RtEaMAfbHe1yC64AJd+DzQP47ORQSR3L2+jA8oDQ9pJ+meiBA6\nAt6AJpI1MsekXleRHnZxacUMVYzdk4c472xst+0ueHyMdaIbWgii54csrDfy2vPw\nZwDNm437wJQvqg4RcUrOd5IoM37UCDfyisU9csY4yMXFxwwKFQtIk4Bn+lGbWdjC\nF+NSS+ujtHl0d5rg12QXegbWtFIol+E/ntxG4uS97dpldO2+cQCMM8RGsXA75teL\n4+HRwu0IEa7aaZZAVDZUA2U6wtAmhFM3\n-----END CERTIFICATE-----",
   284  							Expiry:       "2024-10-20T06:14:29Z",
   285  							KeyAlgorithm: "ECDSA"},
   286  						MultiStackedCertificates: []CertificateObject{},
   287  					},
   288  				},
   289  			},
   290  			expectedHeaders: map[string]string{
   291  				"Accept": "application/vnd.akamai.cps.certificate-history.v2+json",
   292  			},
   293  		},
   294  		"404 not found": {
   295  			request: GetCertificateHistoryRequest{EnrollmentID: 28926000},
   296  			responseBody: `
   297  {
   298      "type": "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found",
   299      "title": "Not Found",
   300      "instance": "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found?id=c3f6bd2c01bc4e138bb5490b0fdb6f5d"
   301  }
   302  `,
   303  			responseStatus: http.StatusNotFound,
   304  			withError: &Error{
   305  				Type:       "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found",
   306  				Title:      "Not Found",
   307  				Instance:   "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found?id=c3f6bd2c01bc4e138bb5490b0fdb6f5d",
   308  				StatusCode: 404,
   309  			},
   310  			expectedPath: "/cps/v2/enrollments/28926000/history/certificates",
   311  		},
   312  		"missing enrollmentID": {
   313  			request:   GetCertificateHistoryRequest{},
   314  			withError: ErrStructValidation,
   315  		},
   316  	}
   317  
   318  	for name, test := range tests {
   319  		t.Run(name, func(t *testing.T) {
   320  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   321  				assert.Equal(t, test.expectedPath, r.URL.String())
   322  				assert.Equal(t, http.MethodGet, r.Method)
   323  				for k, v := range test.expectedHeaders {
   324  					assert.Equal(t, v, r.Header.Get(k))
   325  				}
   326  				w.WriteHeader(test.responseStatus)
   327  				_, err := w.Write([]byte(test.responseBody))
   328  				assert.NoError(t, err)
   329  			}))
   330  			client := mockAPIClient(t, mockServer)
   331  			result, err := client.GetCertificateHistory(context.Background(), test.request)
   332  			if test.withError != nil {
   333  				assert.True(t, errors.Is(err, test.withError), "want: %s; got: %s", test.withError, err)
   334  				return
   335  			}
   336  			require.NoError(t, err)
   337  			assert.Equal(t, test.expectedResponse, result)
   338  		})
   339  	}
   340  }
   341  
   342  func TestGetChangeHistory(t *testing.T) {
   343  	tests := map[string]struct {
   344  		request          GetChangeHistoryRequest
   345  		responseBody     string
   346  		responseStatus   int
   347  		withError        error
   348  		expectedPath     string
   349  		expectedResponse *GetChangeHistoryResponse
   350  		expectedHeaders  map[string]string
   351  	}{
   352  		"200 ok": {
   353  			request: GetChangeHistoryRequest{EnrollmentID: 28926},
   354  			responseBody: `
   355  {
   356      "changes": [
   357          {
   358              "action": "new-certificate",
   359              "actionDescription": "Create New Certificate",
   360              "status": "completed",
   361              "lastUpdated": "2022-07-21T21:40:00Z",
   362              "createdBy": "wzagrajc",
   363              "createdOn": "2022-07-18T12:05:41Z",
   364              "ra": "third-party",
   365              "primaryCertificate": {
   366                  "certificate": "-----BEGIN CERTIFICATE-----\nMIIDpDCCAgygAwIBAgIQG7UFcE+swJPyIGzHEPrWTzANBgkqhkiG9w0BAQsFADCB\njTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTEwLwYDVQQLDCh3emFn\ncmFqY0BrcmstbXAwNmMgKFdvamNpZWNoIFphZ3JhamN6dWspMTgwNgYDVQQDDC9t\na2NlcnQgd3phZ3JhamNAa3JrLW1wMDZjIChXb2pjaWVjaCBaYWdyYWpjenVrKTAe\nFw0yMjA3MjAwNjE0MjlaFw0yNDEwMjAwNjE0MjlaMGsxCzAJBgNVBAYTAlBMMQ0w\nCwYDVQQIEwR0ZXN0MQ0wCwYDVQQHEwR0ZXN0MQ0wCwYDVQQKEwR0ZXN0MQ0wCwYD\nVQQLEwR0ZXN0MSAwHgYDVQQDExdjcHMud3phLXRlc3QwMDEuYWthdGVzdDBZMBMG\nByqGSM49AgEGCCqGSM49AwEHA0IABLAKxSxwaZgqpFVHhFi6feDQ6A8Q0S71p/mP\nuwkUD1zNvmyKPzDflAWDGTyocC8aCzGCHiFdt6CRhCy25RwDK2mjbDBqMA4GA1Ud\nDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBTpiXDW\nLsgNHer9dSJuuzAt6LBpWzAiBgNVHREEGzAZghdjcHMud3phLXRlc3QwMDEuYWth\ndGVzdDANBgkqhkiG9w0BAQsFAAOCAYEAiXmq64svercHBIFbHfNPrRns3ccrOy0U\n+zHks6uSGEO2S/wnAhDtpK2D103SnddRK4WXDF3w0F/GCqrXGmWbKQvzukaUTBbl\neO4Wy36qwc/SypvjzZsPI2q2E6e7uONcB2cL6UA18aEO/w9X4jTfWS5zMV1zPO3N\naozEFRXrziFYGrPAJ9o2RldScHU9stl0TcIiuDWzUqPxvseGBuEMFNtVko590cJA\nl36muto6uC0XV8RtEaMAfbHe1yC64AJd+DzQP47ORQSR3L2+jA8oDQ9pJ+meiBA6\nAt6AJpI1MsekXleRHnZxacUMVYzdk4c472xst+0ueHyMdaIbWgii54csrDfy2vPw\nZwDNm437wJQvqg4RcUrOd5IoM37UCDfyisU9csY4yMXFxwwKFQtIk4Bn+lGbWdjC\nF+NSS+ujtHl0d5rg12QXegbWtFIol+E/ntxG4uS97dpldO2+cQCMM8RGsXA75teL\n4+HRwu0IEa7aaZZAVDZUA2U6wtAmhFM3\n-----END CERTIFICATE-----",
   367                  "trustChain": null,
   368                  "csr": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBJTCBzQIBADBrMQswCQYDVQQGEwJQTDENMAsGA1UECAwEdGVzdDENMAsGA1UE\nBwwEdGVzdDENMAsGA1UECgwEdGVzdDENMAsGA1UECwwEdGVzdDEgMB4GA1UEAwwX\nY3BzLnd6YS10ZXN0MDAxLmFrYXRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\nAASwCsUscGmYKqRVR4RYun3g0OgPENEu9af5j7sJFA9czb5sij8w35QFgxk8qHAv\nGgsxgh4hXbegkYQstuUcAytpoAAwCgYIKoZIzj0EAwIDRwAwRAIgZdTQC7pGlBQj\n5QHwPy/XFpXhgkssPkPGiyU+Ooauq6ACICCb/nP+DaEs193RFm6MBpScJAxf1F0r\ne9qtJn4af/6h\n-----END CERTIFICATE REQUEST-----\n",
   369                  "keyAlgorithm": "ECDSA"
   370              },
   371              "multiStackedCertificates": [],
   372              "primaryCertificateOrderDetails": null,
   373              "businessCaseId": null
   374          }
   375      ]
   376  }
   377  `,
   378  			responseStatus: http.StatusOK,
   379  			expectedPath:   "/cps/v2/enrollments/28926/history/changes",
   380  			expectedResponse: &GetChangeHistoryResponse{
   381  				Changes: []ChangeHistory{{
   382  					Action:            "new-certificate",
   383  					ActionDescription: "Create New Certificate",
   384  					Status:            "completed",
   385  					LastUpdated:       "2022-07-21T21:40:00Z",
   386  					CreatedBy:         "wzagrajc",
   387  					CreatedOn:         "2022-07-18T12:05:41Z",
   388  					RA:                "third-party",
   389  					PrimaryCertificate: CertificateChangeHistory{
   390  						Certificate:  "-----BEGIN CERTIFICATE-----\nMIIDpDCCAgygAwIBAgIQG7UFcE+swJPyIGzHEPrWTzANBgkqhkiG9w0BAQsFADCB\njTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTEwLwYDVQQLDCh3emFn\ncmFqY0BrcmstbXAwNmMgKFdvamNpZWNoIFphZ3JhamN6dWspMTgwNgYDVQQDDC9t\na2NlcnQgd3phZ3JhamNAa3JrLW1wMDZjIChXb2pjaWVjaCBaYWdyYWpjenVrKTAe\nFw0yMjA3MjAwNjE0MjlaFw0yNDEwMjAwNjE0MjlaMGsxCzAJBgNVBAYTAlBMMQ0w\nCwYDVQQIEwR0ZXN0MQ0wCwYDVQQHEwR0ZXN0MQ0wCwYDVQQKEwR0ZXN0MQ0wCwYD\nVQQLEwR0ZXN0MSAwHgYDVQQDExdjcHMud3phLXRlc3QwMDEuYWthdGVzdDBZMBMG\nByqGSM49AgEGCCqGSM49AwEHA0IABLAKxSxwaZgqpFVHhFi6feDQ6A8Q0S71p/mP\nuwkUD1zNvmyKPzDflAWDGTyocC8aCzGCHiFdt6CRhCy25RwDK2mjbDBqMA4GA1Ud\nDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBTpiXDW\nLsgNHer9dSJuuzAt6LBpWzAiBgNVHREEGzAZghdjcHMud3phLXRlc3QwMDEuYWth\ndGVzdDANBgkqhkiG9w0BAQsFAAOCAYEAiXmq64svercHBIFbHfNPrRns3ccrOy0U\n+zHks6uSGEO2S/wnAhDtpK2D103SnddRK4WXDF3w0F/GCqrXGmWbKQvzukaUTBbl\neO4Wy36qwc/SypvjzZsPI2q2E6e7uONcB2cL6UA18aEO/w9X4jTfWS5zMV1zPO3N\naozEFRXrziFYGrPAJ9o2RldScHU9stl0TcIiuDWzUqPxvseGBuEMFNtVko590cJA\nl36muto6uC0XV8RtEaMAfbHe1yC64AJd+DzQP47ORQSR3L2+jA8oDQ9pJ+meiBA6\nAt6AJpI1MsekXleRHnZxacUMVYzdk4c472xst+0ueHyMdaIbWgii54csrDfy2vPw\nZwDNm437wJQvqg4RcUrOd5IoM37UCDfyisU9csY4yMXFxwwKFQtIk4Bn+lGbWdjC\nF+NSS+ujtHl0d5rg12QXegbWtFIol+E/ntxG4uS97dpldO2+cQCMM8RGsXA75teL\n4+HRwu0IEa7aaZZAVDZUA2U6wtAmhFM3\n-----END CERTIFICATE-----",
   391  						CSR:          "-----BEGIN CERTIFICATE REQUEST-----\nMIIBJTCBzQIBADBrMQswCQYDVQQGEwJQTDENMAsGA1UECAwEdGVzdDENMAsGA1UE\nBwwEdGVzdDENMAsGA1UECgwEdGVzdDENMAsGA1UECwwEdGVzdDEgMB4GA1UEAwwX\nY3BzLnd6YS10ZXN0MDAxLmFrYXRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\nAASwCsUscGmYKqRVR4RYun3g0OgPENEu9af5j7sJFA9czb5sij8w35QFgxk8qHAv\nGgsxgh4hXbegkYQstuUcAytpoAAwCgYIKoZIzj0EAwIDRwAwRAIgZdTQC7pGlBQj\n5QHwPy/XFpXhgkssPkPGiyU+Ooauq6ACICCb/nP+DaEs193RFm6MBpScJAxf1F0r\ne9qtJn4af/6h\n-----END CERTIFICATE REQUEST-----\n",
   392  						KeyAlgorithm: "ECDSA",
   393  					},
   394  					MultiStackedCertificates: []CertificateChangeHistory{},
   395  				}},
   396  			},
   397  			expectedHeaders: map[string]string{
   398  				"Accept": "application/vnd.akamai.cps.change-history.v5+json",
   399  			},
   400  		},
   401  		"404 not found": {
   402  			request: GetChangeHistoryRequest{EnrollmentID: 28926000},
   403  			responseBody: `
   404  {
   405      "type": "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found",
   406      "title": "Not Found",
   407      "instance": "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found?id=723cacad466e43688f4a8bd08639ba4e"
   408  }
   409  `,
   410  			responseStatus: http.StatusNotFound,
   411  			withError: &Error{
   412  				Type:       "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found",
   413  				Title:      "Not Found",
   414  				Instance:   "https://akaa-wb66l66toq4ewuc4-haxhlepvmnlgidlc.luna-dev.akamaiapis.net/cps/v2/error-types/not-found?id=723cacad466e43688f4a8bd08639ba4e",
   415  				StatusCode: 404,
   416  			},
   417  			expectedPath: "/cps/v2/enrollments/28926000/history/changes",
   418  		},
   419  		"missing enrollmentID": {
   420  			request:   GetChangeHistoryRequest{},
   421  			withError: ErrStructValidation,
   422  		},
   423  	}
   424  
   425  	for name, test := range tests {
   426  		t.Run(name, func(t *testing.T) {
   427  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   428  				assert.Equal(t, test.expectedPath, r.URL.String())
   429  				assert.Equal(t, http.MethodGet, r.Method)
   430  				for k, v := range test.expectedHeaders {
   431  					assert.Equal(t, v, r.Header.Get(k))
   432  				}
   433  				w.WriteHeader(test.responseStatus)
   434  				_, err := w.Write([]byte(test.responseBody))
   435  				assert.NoError(t, err)
   436  			}))
   437  			client := mockAPIClient(t, mockServer)
   438  			result, err := client.GetChangeHistory(context.Background(), test.request)
   439  			if test.withError != nil {
   440  				assert.True(t, errors.Is(err, test.withError), "want: %s; got: %s", test.withError, err)
   441  				return
   442  			}
   443  			require.NoError(t, err)
   444  			assert.Equal(t, test.expectedResponse, result)
   445  		})
   446  	}
   447  }