diff options
author | Ola Aunrønning <olaa@verizonmedia.com> | 2020-06-25 17:21:11 +0200 |
---|---|---|
committer | Ola Aunrønning <olaa@verizonmedia.com> | 2020-06-26 08:06:35 +0200 |
commit | ec08b79dc7a187d52f90855b91781b2744ac7db3 (patch) | |
tree | 9441a7566118bf9ff30605b12cbd8a97d228566f /controller-server/src/test/java/com | |
parent | 0e441d4aa6ee951367dfbb9aa684b4b556039a13 (diff) |
Add BillingApiHandler
Diffstat (limited to 'controller-server/src/test/java/com')
6 files changed, 318 insertions, 8 deletions
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java index b7e7c9814e3..1b21f7db7c4 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java @@ -12,14 +12,14 @@ import com.yahoo.vespa.hosted.controller.api.integration.aws.MockAwsEventFetcher import com.yahoo.vespa.hosted.controller.api.integration.aws.MockResourceTagger; import com.yahoo.vespa.hosted.controller.api.integration.aws.NoopApplicationRoleService; import com.yahoo.vespa.hosted.controller.api.integration.aws.ResourceTagger; -import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanController; +import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingController; +import com.yahoo.vespa.hosted.controller.api.integration.billing.MockBillingController; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMock; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer; import com.yahoo.vespa.hosted.controller.api.integration.dns.MemoryNameService; import com.yahoo.vespa.hosted.controller.api.integration.entity.MemoryEntityService; import com.yahoo.vespa.hosted.controller.api.integration.organization.MockContactRetriever; import com.yahoo.vespa.hosted.controller.api.integration.organization.MockIssueHandler; -import com.yahoo.vespa.hosted.controller.api.integration.organization.SystemMonitor; import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumerMock; import com.yahoo.vespa.hosted.controller.api.integration.routing.GlobalRoutingService; import com.yahoo.vespa.hosted.controller.api.integration.routing.MemoryGlobalRoutingService; @@ -60,7 +60,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg private final MockRunDataStore mockRunDataStore = new MockRunDataStore(); private final MockResourceTagger mockResourceTagger = new MockResourceTagger(); private final ApplicationRoleService applicationRoleService = new NoopApplicationRoleService(); - private final PlanController planController = (tenantName) -> null; + private final BillingController billingController = new MockBillingController(); public ServiceRegistryMock(SystemName system) { this.zoneRegistryMock = new ZoneRegistryMock(system); @@ -187,6 +187,11 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg return systemMonitor; } + @Override + public BillingController billingController() { + return billingController; + } + public ConfigServerMock configServerMock() { return configServerMock; } @@ -203,9 +208,4 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg return endpointCertificateMock; } - @Override - public PlanController planController() { - return planController; - } - } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java new file mode 100644 index 00000000000..19cfa95c682 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java @@ -0,0 +1,214 @@ +package com.yahoo.vespa.hosted.controller.restapi.billing; + +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.hosted.controller.api.integration.billing.Invoice; +import com.yahoo.vespa.hosted.controller.api.integration.billing.MockBillingController; +import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId; +import com.yahoo.vespa.hosted.controller.api.role.Role; +import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; +import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import static com.yahoo.application.container.handler.Request.Method.*; +import static org.junit.Assert.*; + +/** + * @author olaa + */ +public class BillingApiHandlerTest extends ControllerContainerCloudTest { + + private static final String responseFiles = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/"; + private static final TenantName tenant = TenantName.from("tenant1"); + private static final TenantName tenant2 = TenantName.from("tenant2"); + private static final Set<Role> tenantRole = Set.of(Role.administrator(tenant)); + private static final Set<Role> financeAdmin = Set.of(Role.hostedAccountant()); + private MockBillingController billingController; + + private ContainerTester tester; + + @Before + public void setup() { + tester = new ContainerTester(container, responseFiles); + billingController = (MockBillingController) tester.serviceRegistry().billingController(); + } + + @Override + protected SystemName system() { + return SystemName.PublicCd; + } + + @Override + protected String variablePartXml() { + return " <component id='com.yahoo.vespa.hosted.controller.security.CloudAccessControlRequests'/>\n" + + " <component id='com.yahoo.vespa.hosted.controller.security.CloudAccessControl'/>\n" + + + " <handler id='com.yahoo.vespa.hosted.controller.restapi.billing.BillingApiHandler'>\n" + + " <binding>http://*/billing/v1/*</binding>\n" + + " </handler>\n" + + + " <http>\n" + + " <server id='default' port='8080' />\n" + + " <filtering>\n" + + " <request-chain id='default'>\n" + + " <filter id='com.yahoo.vespa.hosted.controller.restapi.filter.ControllerAuthorizationFilter'/>\n" + + " <binding>http://*/*</binding>\n" + + " </request-chain>\n" + + " </filtering>\n" + + " </http>\n"; + } + + @Test + public void setting_and_deleting_instrument() { + assertTrue(billingController.getDefaultInstrument(tenant).isEmpty()); + + var instrumentRequest = request("/billing/v1/tenant/tenant1/instrument", PATCH) + .data("{\"active\": \"id-1\"}") + .roles(tenantRole); + + tester.assertResponse(instrumentRequest,"OK"); + assertEquals("id-1", billingController.getDefaultInstrument(tenant).get().getId()); + + var deleteInstrumentRequest = request("/billing/v1/tenant/tenant1/instrument/id-1", DELETE) + .roles(tenantRole); + + tester.assertResponse(deleteInstrumentRequest,"OK"); + assertTrue(billingController.getDefaultInstrument(tenant).isEmpty()); + } + + @Test + public void response_list_bills() { + var invoice = createInvoice(); + + billingController.addInvoice(tenant, invoice, true); + billingController.addInvoice(tenant, invoice, false); + billingController.setPlan(tenant, PlanId.from("some-plan"), true); + + var request = request("/billing/v1/tenant/tenant1/billing?until=2020-05-28").roles(tenantRole); + tester.assertResponse(request, new File("tenant-billing-view")); + + } + + @Test + public void test_invoice_creation() { + var invoices = billingController.getInvoices(tenant); + assertEquals(0, invoices.size()); + + String requestBody = "{\"tenant\":\"tenant1\", \"startTime\":\"2020-04-20\", \"endTime\":\"2020-05-20\"}"; + var request = request("/billing/v1/invoice", POST) + .data(requestBody) + .roles(tenantRole); + + tester.assertResponse(request, accessDenied, 403); + request.roles(financeAdmin); + tester.assertResponse(request, new File("invoice-creation-response")); + + invoices = billingController.getInvoices(tenant); + assertEquals(1, invoices.size()); + Invoice invoice = invoices.get(0); + assertEquals(invoice.getStartTime().toString(), "2020-04-20T00:00Z[UTC]"); + assertEquals(invoice.getEndTime().toString(), "2020-05-20T00:00Z[UTC]"); + } + + @Test + public void adding_and_listing_line_item() { + + var requestBody = "{" + + "\"description\":\"some description\"," + + "\"amount\":\"123.45\" " + + "}"; + + var request = request("/billing/v1/invoice/tenant/tenant1/line-item", POST) + .data(requestBody) + .roles(financeAdmin); + + tester.assertResponse(request, "{\"message\":\"Added line item for tenant tenant1\"}"); + + var lineItems = billingController.getUnusedLineItems(tenant); + Assert.assertEquals(1, lineItems.size()); + Invoice.LineItem lineItem = lineItems.get(0); + assertEquals("some description", lineItem.description()); + assertEquals(new BigDecimal("123.45"), lineItem.amount()); + + request = request("/billing/v1/invoice/tenant/tenant1/line-item") + .roles(financeAdmin); + + tester.assertResponse(request, new File("line-item-list")); + } + + @Test + public void adding_new_status() { + billingController.addInvoice(tenant, createInvoice(), true); + + var requestBody = "{\"status\":\"DONE\"}"; + var request = request("/billing/v1/invoice/id-1/status", POST) + .data(requestBody) + .roles(financeAdmin); + tester.assertResponse(request, "{\"message\":\"Updated status of invoice id-1\"}"); + + var invoice = billingController.getInvoices(tenant).get(0); + assertEquals("DONE", invoice.status()); + } + + @Test + public void list_all_uninvoiced_items() { + var invoice = createInvoice(); + billingController.setPlan(tenant, PlanId.from("some-plan"), true); + billingController.setPlan(tenant2, PlanId.from("some-plan"), true); + billingController.addInvoice(tenant, invoice, false); + billingController.addLineItem(tenant, "support", new BigDecimal("42"), "Smith"); + billingController.addInvoice(tenant2, invoice, false); + + + var request = request("/billing/v1/billing?until=2020-05-28").roles(financeAdmin); + + tester.assertResponse(request, new File("billing-all-tenants")); + } + + @Test + public void setting_plans() { + var planRequest = request("/billing/v1/tenant/tenant1/plan", PATCH) + .data("{\"plan\": \"new-plan\"}") + .roles(tenantRole); + tester.assertResponse(planRequest, "Plan: new-plan"); + assertEquals("new-plan", billingController.getPlan(tenant).value()); + } + + private Invoice createInvoice() { + var start = LocalDate.of(2020, 5, 23).atStartOfDay(ZoneId.systemDefault()); + var end = start.plusDays(5); + var statusHistory = new Invoice.StatusHistory(new TreeMap<>(Map.of(start, "OPEN"))); + return new Invoice( + Invoice.Id.of("id-1"), + statusHistory, + List.of(createLineItem(start)), + start, + end + ); + } + + + private Invoice.LineItem createLineItem(ZonedDateTime addedAt) { + return new Invoice.LineItem( + "some-id", + "description", + new BigDecimal("123.00"), + "plan", + "Smith", + addedAt + ); + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/billing-all-tenants b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/billing-all-tenants new file mode 100644 index 00000000000..c5bf0c88c2c --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/billing-all-tenants @@ -0,0 +1,48 @@ +{ + "until":"2020-05-28", + "tenants":[ + { + "tenant":"tenant2", + "plan":"some-plan", + "current":{ + "amount":"123.00", + "status":"accrued", + "from":"2020-05-23", + "items":[ + { + "id":"some-id", + "description":"description", + "amount":"123.00" + } + ] + }, + "additional":{"items":[]} + }, + { + "tenant":"tenant1", + "plan":"some-plan", + "current":{ + "amount":"123.00", + "status":"accrued", + "from":"2020-05-23", + "items":[ + { + "id":"some-id", + "description":"description", + "amount":"123.00" + } + ] + }, + "additional": + { + "items":[ + { + "id":"line-item-id", + "description":"support", + "amount":"42.00" + } + ] + } + } + ] +}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/invoice-creation-response b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/invoice-creation-response new file mode 100644 index 00000000000..0a92229025b --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/invoice-creation-response @@ -0,0 +1 @@ +{"message":"Created invoice with ID id-123"}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/line-item-list b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/line-item-list new file mode 100644 index 00000000000..cd5aec2f8f4 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/line-item-list @@ -0,0 +1,9 @@ +{ + "lineItems":[ + { + "id":"line-item-id", + "description":"some description", + "amount":"123.45" + } + ] +}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view new file mode 100644 index 00000000000..8bc39771b31 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view @@ -0,0 +1,38 @@ +{ + "until":"2020-05-28", + "plan":"some-plan", + "current":{ + "amount":"123.00", + "status":"accrued", + "from":"2020-05-23", + "items":[ + { + "id":"some-id", + "description":"description", + "amount":"123.00" + } + ] + }, + "additional":{"items":[]}, + "bills":[ + { + "id":"id-1", + "from":"2020-05-23", + "to":"2020-05-28","amount":"123.00", + "status":"OPEN", + "statusHistory":[ + { + "at":"2020-05-23", + "status":"OPEN" + } + ], + "items":[ + { + "id":"some-id", + "description":"description", + "amount":"123.00" + } + ] + } + ] +}
\ No newline at end of file |