From e173566c792413fcfe755ab73a2c6078ec2bd3eb Mon Sep 17 00:00:00 2001 From: sajit Date: Fri, 4 Oct 2024 16:55:39 -0400 Subject: [PATCH] Add unit tests --- mongodb-atlas/cmd/main.go | 15 ++- mongodb-atlas/cmd/main_test.go | 186 +++++++++++++++++++++++++++++---- 2 files changed, 172 insertions(+), 29 deletions(-) diff --git a/mongodb-atlas/cmd/main.go b/mongodb-atlas/cmd/main.go index b9dddd9..5123145 100644 --- a/mongodb-atlas/cmd/main.go +++ b/mongodb-atlas/cmd/main.go @@ -134,7 +134,7 @@ func (a *AtlasCostSource) getAtlasCostsForWindow(win *opencost.Window) (*pb.Cust } // get the costs - costs, err := getCosts(a.atlasClient, a.orgID, token) + costs, err := GetCosts(a.atlasClient, a.orgID, token) if err != nil { log.Errorf("error getting costs: %v", err) return nil, err @@ -154,7 +154,7 @@ func (a *AtlasCostSource) getAtlasCostsForWindow(win *opencost.Window) (*pb.Cust return &resp, nil } -func getCosts(client HTTPClient, org string, token string) ([]*pb.CustomCost, error) { +func GetCosts(client HTTPClient, org string, token string) ([]*pb.CustomCost, error) { request, _ := http.NewRequest("GET", fmt.Sprintf(costExplorerQueryFmt, org, token), nil) request.Header.Set("Accept", "application/vnd.atlas.2023-01-01+json") @@ -163,7 +163,7 @@ func getCosts(client HTTPClient, org string, token string) ([]*pb.CustomCost, er response, error := client.Do(request) statusCode := response.StatusCode //102 status code means processing - so repeat call 5 times to see if we get a response - for count := 1; count < 5 && statusCode == 102; count++ { + for count := 1; count < 5 && statusCode == http.StatusProcessing; count++ { // Sleep for 5 seconds before the next request time.Sleep(5 * time.Second) response, _ := client.Do(request) @@ -171,6 +171,10 @@ func getCosts(client HTTPClient, org string, token string) ([]*pb.CustomCost, er } + if statusCode == http.StatusProcessing { + msg := "timeout waiting for response" + return nil, fmt.Errorf(msg) + } if error != nil { msg := fmt.Sprintf("getCostExplorerUsage: error from server: %v", error) log.Errorf(msg) @@ -187,11 +191,6 @@ func getCosts(client HTTPClient, org string, token string) ([]*pb.CustomCost, er log.Errorf(msg) return nil, fmt.Errorf(msg) } - //sample - //report_data='{"usageDetails":[{"invoiceId":"66d7254246a21a41036ff315","organizationId":"66d7254246a21a41036ff2e9","organizationName":"Kubecost","service":"Data Transfer","usageAmount":1.33,"usageDate":"2024-09-01"}, - //{"invoiceId":"66d7254246a21a41036ff315","organizationId":"66d7254246a21a41036ff2e9","organizationName":"Kubecost","service":"Clusters","usageAmount":51.19,"usageDate":"2024-09-01"}]}’ - - //fake it for now var costs []*pb.CustomCost // Iterate over the UsageDetails in CostResponse for _, invoice := range costResponse.UsageDetails { diff --git a/mongodb-atlas/cmd/main_test.go b/mongodb-atlas/cmd/main_test.go index da7bc46..50c3944 100644 --- a/mongodb-atlas/cmd/main_test.go +++ b/mongodb-atlas/cmd/main_test.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "testing" "time" @@ -23,10 +23,6 @@ func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) { return m.DoFunc(req) } -func TestSanity(t *testing.T) { - assert.True(t, true) -} - func TestCreateCostExplorerQueryToken(t *testing.T) { // Mock data org := "testOrg" @@ -55,7 +51,7 @@ func TestCreateCostExplorerQueryToken(t *testing.T) { // Return a mock response with status 200 and mock JSON body return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewBuffer(mockResponseJson)), + Body: io.NopCloser(bytes.NewBuffer(mockResponseJson)), }, nil }, } @@ -119,19 +115,11 @@ func TestErrorFromServer(t *testing.T) { // Create a mock HTTPClient that returns a successful response mockClient := &MockHTTPClient{ DoFunc: func(req *http.Request) (*http.Response, error) { - // Verify that the request method and URL are correct - if req.Method != http.MethodPost { - t.Errorf("expected POST request, got %s", req.Method) - } - expectedURL := fmt.Sprintf(costExplorerFmt, orgId) - if req.URL.String() != expectedURL { - t.Errorf("expected URL %s, got %s", expectedURL, req.URL.String()) - } // Return a mock response with status 200 and mock JSON body return &http.Response{ StatusCode: http.StatusInternalServerError, - Body: ioutil.NopCloser(bytes.NewBufferString("fake")), + Body: io.NopCloser(bytes.NewBufferString("fake")), }, nil }, } @@ -147,13 +135,62 @@ func TestCallToCreateCostExplorerQueryBadMessage(t *testing.T) { layout := "2006-01-02" endTime, _ := time.Parse(layout, "2024-07-01") startTime, _ := time.Parse(layout, "2023-12-01") + mockClient := &MockHTTPClient{ + DoFunc: func(req *http.Request) (*http.Response, error) { + + // Return a mock response with status 200 and mock JSON body + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewBufferString("This ain't json")), + }, nil + }, + } + + _, error := CreateCostExplorerQueryToken("myOrg", startTime, endTime, mockClient) + assert.NotEmpty(t, error) + +} + +// tests for getCosts +func TestGetCostsMultipleInvoices(t *testing.T) { + costResponse := atlasplugin.CostResponse{ + UsageDetails: []atlasplugin.Invoice{ + { + InvoiceId: "INV001", + OrganizationId: "ORG123", + OrganizationName: "Acme Corp", + Service: "Compute", + UsageAmount: 120.50, + UsageDate: "2024-10-01", + }, + { + InvoiceId: "INV002", + OrganizationId: "ORG124", + OrganizationName: "Beta Corp", + Service: "Storage", + UsageAmount: 75.75, + UsageDate: "2024-10-02", + }, + { + InvoiceId: "INV003", + OrganizationId: "ORG125", + OrganizationName: "Gamma Inc", + Service: "Networking", + UsageAmount: 50.00, + UsageDate: "2024-10-03", + }, + }, + } + + mockResponseJson, _ := json.Marshal(costResponse) + mockClient := &MockHTTPClient{ DoFunc: func(req *http.Request) (*http.Response, error) { // Verify that the request method and URL are correct - if req.Method != http.MethodPost { - t.Errorf("expected POST request, got %s", req.Method) + if req.Method != http.MethodGet { + t.Errorf("expected GET request, got %s", req.Method) } - expectedURL := fmt.Sprintf(costExplorerFmt, "myOrg") + expectedURL := fmt.Sprintf(costExplorerQueryFmt, "myOrg", "t1") if req.URL.String() != expectedURL { t.Errorf("expected URL %s, got %s", expectedURL, req.URL.String()) } @@ -161,14 +198,121 @@ func TestCallToCreateCostExplorerQueryBadMessage(t *testing.T) { // Return a mock response with status 200 and mock JSON body return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewBufferString("This ain't json")), + Body: io.NopCloser(bytes.NewBuffer(mockResponseJson)), }, nil }, } + costs, err := GetCosts(mockClient, "myOrg", "t1") + assert.Nil(t, err) + assert.Equal(t, 3, len(costs)) - _, error := CreateCostExplorerQueryToken("myOrg", startTime, endTime, mockClient) + for i, invoice := range costResponse.UsageDetails { + assert.Equal(t, invoice.InvoiceId, costs[i].Id) + assert.Equal(t, invoice.OrganizationName, costs[i].AccountName) + assert.Equal(t, invoice.Service, costs[i].ChargeCategory) + assert.Equal(t, invoice.UsageAmount, costs[i].BilledCost) + } +} + +func TestGetCostErrorFromServer(t *testing.T) { + + mockClient := &MockHTTPClient{ + DoFunc: func(req *http.Request) (*http.Response, error) { + + // Return a mock response with status 200 and mock JSON body + return &http.Response{ + StatusCode: http.StatusInternalServerError, + Body: io.NopCloser(bytes.NewBufferString("")), + }, nil + }, + } + costs, err := GetCosts(mockClient, "myOrg", "t1") + + assert.NotEmpty(t, err) + assert.Nil(t, costs) + +} + +func TestGetCostsBadMessage(t *testing.T) { + + mockClient := &MockHTTPClient{ + DoFunc: func(req *http.Request) (*http.Response, error) { + + // Return a mock response with status 200 and mock JSON body + return &http.Response{ + StatusCode: http.StatusInternalServerError, + Body: io.NopCloser(bytes.NewBufferString("No Jason No")), + }, nil + }, + } + + _, error := GetCosts(mockClient, "myOrg", "t1") assert.NotEmpty(t, error) } -//tests for getCosts +func TestRepeatCallTill200(t *testing.T) { + + var count = 0 + costResponse := atlasplugin.CostResponse{ + UsageDetails: []atlasplugin.Invoice{ + + { + InvoiceId: "INV003", + OrganizationId: "ORG125", + OrganizationName: "Gamma Inc", + Service: "Networking", + UsageAmount: 50.00, + UsageDate: "2024-10-03", + }, + }, + } + + mockResponseJson, _ := json.Marshal(costResponse) + + mockClient := &MockHTTPClient{ + DoFunc: func(req *http.Request) (*http.Response, error) { + count++ + + if count < 5 { + // Return a mock response with status 200 and mock JSON body + return &http.Response{ + StatusCode: http.StatusProcessing, + Body: io.NopCloser(bytes.NewBuffer(mockResponseJson)), + }, nil + + } else { + // Return a mock response with status 200 and mock JSON body + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewBuffer(mockResponseJson)), + }, nil + + } + + }, + } + + costs, err := GetCosts(mockClient, "myOrg", "t1") + assert.Nil(t, err) + assert.Equal(t, 1, len(costs)) +} + +func TestStuckInProcessing(t *testing.T) { + + mockClient := &MockHTTPClient{ + DoFunc: func(req *http.Request) (*http.Response, error) { + + // Return a mock response with status 200 and mock JSON body + return &http.Response{ + StatusCode: http.StatusProcessing, + Body: io.NopCloser(bytes.NewBufferString("")), + }, nil + + }, + } + + costs, err := GetCosts(mockClient, "myOrg", "t1") + assert.NotNil(t, err) + assert.Nil(t, costs) +}