From d2121028697790eb97d0397e198c6789c889c9e4 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Mon, 7 Oct 2024 14:10:21 -0700 Subject: [PATCH 1/4] inital work on aws billing stubs, not working yet #76 --- .gitignore | 3 + DESCRIPTION | 3 +- tests/testthat/helper-stubs.R | 268 ++++++++++++++++++++++++++++++++++ tests/testthat/test-billing.R | 29 ++++ 4 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 tests/testthat/helper-stubs.R create mode 100644 tests/testthat/test-billing.R diff --git a/.gitignore b/.gitignore index 4bb88bc..3960f12 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ tests/fixtures/six_bucket*.yml tests/fixtures/aws_policy_attach*.yml tests/fixtures/aws_policy_detach*.yml tests/fixtures/aws_policy_list_entities*.yml +tests/fixtures/billing* +tests/fixtures/blob* +tests/fixtures/*blob* diff --git a/DESCRIPTION b/DESCRIPTION index 72331a9..f2257f6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -49,7 +49,8 @@ Suggests: testthat (>= 3.0.0), vcr (>= 0.6.0), withr, - ggplot2 + ggplot2, + webmockr Config/roxyglobals/filename: globals.R Config/roxyglobals/unique: FALSE Config/testthat/edition: 3 diff --git a/tests/testthat/helper-stubs.R b/tests/testthat/helper-stubs.R new file mode 100644 index 0000000..f4efdd3 --- /dev/null +++ b/tests/testthat/helper-stubs.R @@ -0,0 +1,268 @@ +library(webmockr) + +# vcr::vcr_configure(dir = "tests/fixtures") +# cas <- vcr::use_cassette("billing_last_two_days", { +# library(lubridate) +# start_date <- today() - days(2) +# aws_billing(date_start = start_date) +# }) + +# cas_yaml <- yaml::yaml.load_file(cas$file()) +# responses <- vapply(cas_yaml$http_interactions, \(w) w$response$body$string, "") + +# random_int <- function() { +# as.character(round(runif(1, 10^10, 10^12))) +# } + +# library(glue) +# library(jqr) + +# randomize_json <- function(json_str, which) { +# jq(json_str, ".DimensionValueAttributes[].Attributes.description |= \"some-org\"") %>% +# jq(glue(".DimensionValueAttributes[].Attributes.value |= \"{random_int()}\"")) %>% +# jq(glue(".DimensionValueAttributes[].Value |= \"{random_int()}\"")) %>% +# jq(glue(".ResultsByTime[].Groups[].Keys[1] |= \"{random_int()}\"")) %>% +# jq(glue(".ResultsByTime[].Groups[].Metrics.{which}.Amount |= \"{round(runif(1, 0, 10), digits = 8)}\"")) +# } +# randomize_json(responses[1], "UnblendedCost") %>% jsonlite::prettify(indent = 2) +# randomize_json(responses[2], "BlendedCost") %>% jsonlite::prettify(indent = 2) + +response_billing_unblended_1 <- '{ + "DimensionValueAttributes": [ + { + "Attributes": { + "description": "some-org", + "value": "946728054637" + }, + "Value": "336503573358" + } + ], + "GroupDefinitions": [ + { + "Key": "SERVICE", + "Type": "DIMENSION" + }, + { + "Key": "LINKED_ACCOUNT", + "Type": "DIMENSION" + } + ], + "ResultsByTime": [ + { + "Estimated": true, + "Groups": [ + { + "Keys": [ + "AWS Cost Explorer", + "224587672609" + ], + "Metrics": { + "UnblendedCost": { + "Amount": "4.41095285", + "Unit": "USD" + } + } + }, + { + "Keys": [ + "AWS Secrets Manager", + "224587672609" + ], + "Metrics": { + "UnblendedCost": { + "Amount": "4.41095285", + "Unit": "USD" + } + } + }, + { + "Keys": [ + "Amazon Simple Storage Service", + "224587672609" + ], + "Metrics": { + "UnblendedCost": { + "Amount": "4.41095285", + "Unit": "USD" + } + } + } + ], + "TimePeriod": { + "End": "2024-10-04", + "Start": "2024-10-03" + }, + "Total": { + + } + }, + { + "Estimated": true, + "Groups": [ + { + "Keys": [ + "AWS Cost Explorer", + "224587672609" + ], + "Metrics": { + "UnblendedCost": { + "Amount": "4.41095285", + "Unit": "USD" + } + } + }, + { + "Keys": [ + "AWS Secrets Manager", + "224587672609" + ], + "Metrics": { + "UnblendedCost": { + "Amount": "4.41095285", + "Unit": "USD" + } + } + }, + { + "Keys": [ + "Amazon Simple Storage Service", + "224587672609" + ], + "Metrics": { + "UnblendedCost": { + "Amount": "4.41095285", + "Unit": "USD" + } + } + } + ], + "TimePeriod": { + "End": "2024-10-05", + "Start": "2024-10-04" + }, + "Total": { + + } + } + ] +}' + +response_billing_blended_1 <- '{ + "DimensionValueAttributes": [ + { + "Attributes": { + "description": "some-org", + "value": "787333819538" + }, + "Value": "988949372892" + } + ], + "GroupDefinitions": [ + { + "Key": "SERVICE", + "Type": "DIMENSION" + }, + { + "Key": "LINKED_ACCOUNT", + "Type": "DIMENSION" + } + ], + "ResultsByTime": [ + { + "Estimated": true, + "Groups": [ + { + "Keys": [ + "AWS Cost Explorer", + "62859488630" + ], + "Metrics": { + "BlendedCost": { + "Amount": "7.11790631", + "Unit": "USD" + } + } + }, + { + "Keys": [ + "AWS Secrets Manager", + "62859488630" + ], + "Metrics": { + "BlendedCost": { + "Amount": "7.11790631", + "Unit": "USD" + } + } + }, + { + "Keys": [ + "Amazon Simple Storage Service", + "62859488630" + ], + "Metrics": { + "BlendedCost": { + "Amount": "7.11790631", + "Unit": "USD" + } + } + } + ], + "TimePeriod": { + "End": "2024-10-04", + "Start": "2024-10-03" + }, + "Total": { + + } + }, + { + "Estimated": true, + "Groups": [ + { + "Keys": [ + "AWS Cost Explorer", + "62859488630" + ], + "Metrics": { + "BlendedCost": { + "Amount": "7.11790631", + "Unit": "USD" + } + } + }, + { + "Keys": [ + "AWS Secrets Manager", + "62859488630" + ], + "Metrics": { + "BlendedCost": { + "Amount": "7.11790631", + "Unit": "USD" + } + } + }, + { + "Keys": [ + "Amazon Simple Storage Service", + "62859488630" + ], + "Metrics": { + "BlendedCost": { + "Amount": "7.11790631", + "Unit": "USD" + } + } + } + ], + "TimePeriod": { + "End": "2024-10-05", + "Start": "2024-10-04" + }, + "Total": { + + } + } + ] +}' diff --git a/tests/testthat/test-billing.R b/tests/testthat/test-billing.R new file mode 100644 index 0000000..1941435 --- /dev/null +++ b/tests/testthat/test-billing.R @@ -0,0 +1,29 @@ +test_that("aws_billing", { + stub_request("post", "https://ce.us-east-1.amazonaws.com") |> + # wi_th(body = list(Metrics = c("UnblendedCost"))) |> + wi_th(body = fromJSON('{"TimePeriod":{"Start":"2024-10-03","End":"2024-10-05"},"Granularity":"DAILY","Metrics":["UnblendedCost"],"GroupBy":[{"Type":"DIMENSION","Key":"SERVICE"},{"Type":"DIMENSION","Key":"LINKED_ACCOUNT"}]}', FALSE)) |> + to_return( + body = jsonlite::minify(response_billing_unblended_1), + status = 200L, + headers = list("Content-type" = "application/x-amz-json-1.1") + ) + stub_request("post", "https://ce.us-east-1.amazonaws.com") |> + # wi_th(body = list(Metrics = c("BlendedCost"))) |> + wi_th(body = fromJSON('{"TimePeriod":{"Start":"2024-10-03","End":"2024-10-05"},"Granularity":"DAILY","Metrics":["BlendedCost"],"GroupBy":[{"Type":"DIMENSION","Key":"SERVICE"},{"Type":"DIMENSION","Key":"LINKED_ACCOUNT"}]}', FALSE)) |> + to_return( + body = jsonlite::minify(response_billing_blended_1), + status = 200L, + headers = list("Content-type" = "application/x-amz-json-1.1") + ) + + enable(quiet = TRUE) + + aws_billing(date_start = "2024-10-03", date_end = "2024-10-05") + + expect_type(start_res, "list") + expect_type(start_res$job_id, "character") + expect_type(start_res$info, "character") + + stub_registry_clear() + disable(quiet = TRUE) +}) From 6c36e09669e6686229657e6fb6959df79f8f945f Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 10 Oct 2024 10:07:16 -0700 Subject: [PATCH 2/4] change localstack url --- R/constants.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/constants.R b/R/constants.R index edd8dac..88eac87 100644 --- a/R/constants.R +++ b/R/constants.R @@ -1,7 +1,7 @@ # nolint start MINIO_USER_PWD <- "minioadmin" MINIO_ENDPOINT <- "http://127.0.0.1:9000" -LOCALSTACK_ENDPOINT <- "http://localhost.localstack.cloud:4566" +LOCALSTACK_ENDPOINT <- "http://localhost:4566" LOCALSTACK_KEY <- "NOTAREALKEY" LOCALSTACK_SECRET <- "AREALLYFAKETOKEN" # nolint end From 8ca5b7c703df4236cbdaa129864eb8fbaa62cb2b Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 10 Oct 2024 10:08:47 -0700 Subject: [PATCH 3/4] add dev webmockr to description --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index f2257f6..8125c9f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -51,6 +51,7 @@ Suggests: withr, ggplot2, webmockr +Remotes: ropensci/webmockr@including Config/roxyglobals/filename: globals.R Config/roxyglobals/unique: FALSE Config/testthat/edition: 3 From 00f04104d7100ff19f8709c872d9d091447ec202 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 10 Oct 2024 10:09:27 -0700 Subject: [PATCH 4/4] remove webmockr load from stubs file and only load webmockr in a withr block in the billing test file to avoid messing with vcr package --- tests/testthat/helper-stubs.R | 2 -- tests/testthat/test-billing.R | 49 +++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/tests/testthat/helper-stubs.R b/tests/testthat/helper-stubs.R index f4efdd3..ffc59a3 100644 --- a/tests/testthat/helper-stubs.R +++ b/tests/testthat/helper-stubs.R @@ -1,5 +1,3 @@ -library(webmockr) - # vcr::vcr_configure(dir = "tests/fixtures") # cas <- vcr::use_cassette("billing_last_two_days", { # library(lubridate) diff --git a/tests/testthat/test-billing.R b/tests/testthat/test-billing.R index 1941435..bf9a748 100644 --- a/tests/testthat/test-billing.R +++ b/tests/testthat/test-billing.R @@ -1,29 +1,32 @@ test_that("aws_billing", { - stub_request("post", "https://ce.us-east-1.amazonaws.com") |> - # wi_th(body = list(Metrics = c("UnblendedCost"))) |> - wi_th(body = fromJSON('{"TimePeriod":{"Start":"2024-10-03","End":"2024-10-05"},"Granularity":"DAILY","Metrics":["UnblendedCost"],"GroupBy":[{"Type":"DIMENSION","Key":"SERVICE"},{"Type":"DIMENSION","Key":"LINKED_ACCOUNT"}]}', FALSE)) |> - to_return( - body = jsonlite::minify(response_billing_unblended_1), - status = 200L, - headers = list("Content-type" = "application/x-amz-json-1.1") - ) - stub_request("post", "https://ce.us-east-1.amazonaws.com") |> - # wi_th(body = list(Metrics = c("BlendedCost"))) |> - wi_th(body = fromJSON('{"TimePeriod":{"Start":"2024-10-03","End":"2024-10-05"},"Granularity":"DAILY","Metrics":["BlendedCost"],"GroupBy":[{"Type":"DIMENSION","Key":"SERVICE"},{"Type":"DIMENSION","Key":"LINKED_ACCOUNT"}]}', FALSE)) |> - to_return( - body = jsonlite::minify(response_billing_blended_1), - status = 200L, - headers = list("Content-type" = "application/x-amz-json-1.1") - ) + withr::with_package("webmockr", { + ## Two stubs below are needed b/c aws_billing makes two http requests + ## in one function call, for unblended and blended costs + stub_request("post", "https://ce.us-east-1.amazonaws.com") |> + wi_th(body = including(list(Metrics = list("UnblendedCost")))) |> + to_return( + body = jsonlite::minify(response_billing_unblended_1), + status = 200L, + headers = list("Content-type" = "application/x-amz-json-1.1") + ) - enable(quiet = TRUE) + stub_request("post", "https://ce.us-east-1.amazonaws.com") |> + wi_th(body = including(list(Metrics = list("BlendedCost")))) |> + to_return( + body = jsonlite::minify(response_billing_blended_1), + status = 200L, + headers = list("Content-type" = "application/x-amz-json-1.1") + ) - aws_billing(date_start = "2024-10-03", date_end = "2024-10-05") + enable(quiet = TRUE) - expect_type(start_res, "list") - expect_type(start_res$job_id, "character") - expect_type(start_res$info, "character") + out <- aws_billing(date_start = "2024-10-03", date_end = "2024-10-05") - stub_registry_clear() - disable(quiet = TRUE) + expect_s3_class(out, "tbl") + expect_equal(NCOL(out), 6) + expect_equal(sort(unique(out$id)), c("blended", "unblended")) + + stub_registry_clear() + disable(quiet = TRUE) + }) })