aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java')
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java93
1 files changed, 82 insertions, 11 deletions
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java
index c5c70998624..4056459c532 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java
@@ -1,22 +1,32 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.test.ManualClock;
+import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
+import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMailer;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
+import com.yahoo.vespa.hosted.controller.notification.Notification;
+import com.yahoo.vespa.hosted.controller.notification.NotificationSource;
import com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import org.junit.jupiter.api.Test;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@@ -24,6 +34,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*/
public class CloudTrialExpirerTest {
+ private static final boolean OVERWRITE_TEST_FILES = false;
+
private final ControllerTester tester = new ControllerTester(SystemName.PublicCd);
private final DeploymentTester deploymentTester = new DeploymentTester(tester);
private final CloudTrialExpirer expirer = new CloudTrialExpirer(tester.controller(), Duration.ofMinutes(5));
@@ -31,28 +43,28 @@ public class CloudTrialExpirerTest {
@Test
void expire_inactive_tenant() {
registerTenant("trial-tenant", "trial", Duration.ofDays(14).plusMillis(1));
- assertEquals(1.0, expirer.maintain());
+ assertEquals(0.0, expirer.maintain());
assertPlan("trial-tenant", "none");
}
@Test
void tombstone_inactive_none() {
- registerTenant("none-tenant", "none", Duration.ofDays(183).plusMillis(1));
- assertEquals(1.0, expirer.maintain());
+ registerTenant("none-tenant", "none", Duration.ofDays(91).plusMillis(1));
+ assertEquals(0.0, expirer.maintain());
assertEquals(Tenant.Type.deleted, tester.controller().tenants().get(TenantName.from("none-tenant"), true).get().type());
}
@Test
void keep_inactive_nontrial_tenants() {
registerTenant("not-a-trial-tenant", "pay-as-you-go", Duration.ofDays(30));
- assertEquals(1.0, expirer.maintain());
+ assertEquals(0.0, expirer.maintain());
assertPlan("not-a-trial-tenant", "pay-as-you-go");
}
@Test
void keep_active_trial_tenants() {
registerTenant("active-trial-tenant", "trial", Duration.ofHours(14).minusMillis(1));
- assertEquals(1.0, expirer.maintain());
+ assertEquals(0.0, expirer.maintain());
assertPlan("active-trial-tenant", "trial");
}
@@ -60,7 +72,7 @@ public class CloudTrialExpirerTest {
void keep_inactive_exempt_tenants() {
registerTenant("exempt-trial-tenant", "trial", Duration.ofDays(40));
((InMemoryFlagSource) tester.controller().flagSource()).withListFlag(PermanentFlags.EXTENDED_TRIAL_TENANTS.id(), List.of("exempt-trial-tenant"), String.class);
- assertEquals(1.0, expirer.maintain());
+ assertEquals(0.0, expirer.maintain());
assertPlan("exempt-trial-tenant", "trial");
}
@@ -68,7 +80,7 @@ public class CloudTrialExpirerTest {
void keep_inactive_trial_tenants_with_deployments() {
registerTenant("with-deployments", "trial", Duration.ofDays(30));
registerDeployment("with-deployments", "my-app", "default");
- assertEquals(1.0, expirer.maintain());
+ assertEquals(0.0, expirer.maintain());
assertPlan("with-deployments", "trial");
}
@@ -76,19 +88,71 @@ public class CloudTrialExpirerTest {
void delete_tenants_with_applications_with_no_deployments() {
registerTenant("with-apps", "trial", Duration.ofDays(184));
tester.createApplication("with-apps", "app1", "instance1");
- assertEquals(1.0, expirer.maintain());
+ assertEquals(0.0, expirer.maintain());
assertPlan("with-apps", "none");
- assertEquals(1.0, expirer.maintain());
+ assertEquals(0.0, expirer.maintain());
assertTrue(tester.controller().tenants().get("with-apps").isEmpty());
}
@Test
void keep_tenants_without_applications_that_are_idle() {
registerTenant("active", "none", Duration.ofDays(182));
- assertEquals(1.0, expirer.maintain());
+ assertEquals(0.0, expirer.maintain());
assertPlan("active", "none");
}
+ @Test
+ void queues_trial_notification_based_on_account_age() throws IOException {
+ var clock = (ManualClock)tester.controller().clock();
+ var mailer = (MockMailer) tester.serviceRegistry().mailer();
+ var tenant = TenantName.from("trial-tenant");
+ ((InMemoryFlagSource) tester.controller().flagSource())
+ .withBooleanFlag(Flags.CLOUD_TRIAL_NOTIFICATIONS.id(), true);
+ registerTenant(tenant.value(), "trial", Duration.ZERO);
+ assertEquals(0.0, expirer.maintain());
+ var expected = "Welcome to Vespa Cloud trial! [Manage plan](https://console.tld/tenant/trial-tenant/account/billing)";
+ assertEquals(expected, lastAccountLevelNotificationTitle(tenant));
+ assertLastEmailEquals(mailer, "welcome.html");
+
+ expected = "You're halfway through the **14 day** trial period. [Manage plan](https://console.tld/tenant/trial-tenant/account/billing)";
+ clock.advance(Duration.ofDays(7));
+ assertEquals(0.0, expirer.maintain());
+ assertEquals(expected, lastAccountLevelNotificationTitle(tenant));
+ assertLastEmailEquals(mailer, "trial-reminder.html");
+
+ expected = "Your Vespa Cloud trial expires in **2** days. [Manage plan](https://console.tld/tenant/trial-tenant/account/billing)";
+ clock.advance(Duration.ofDays(5));
+ assertEquals(0.0, expirer.maintain());
+ assertEquals(expected, lastAccountLevelNotificationTitle(tenant));
+ assertLastEmailEquals(mailer, "trial-expiring-soon.html");
+
+ expected = "Your Vespa Cloud trial expires **tomorrow**. [Manage plan](https://console.tld/tenant/trial-tenant/account/billing)";
+ clock.advance(Duration.ofDays(1));
+ assertEquals(0.0, expirer.maintain());
+ assertEquals(expected, lastAccountLevelNotificationTitle(tenant));
+ assertLastEmailEquals(mailer, "trial-expiring-immediately.html");
+
+ expected = "Your Vespa Cloud trial has expired. [Upgrade plan](https://console.tld/tenant/trial-tenant/account/billing)";
+ clock.advance(Duration.ofDays(2));
+ assertEquals(0.0, expirer.maintain());
+ assertEquals(expected, lastAccountLevelNotificationTitle(tenant));
+ assertLastEmailEquals(mailer, "trial-expired.html");
+ }
+
+ private void assertLastEmailEquals(MockMailer mailer, String expectedContentFile) throws IOException {
+ var mails = mailer.inbox("dev-trial-tenant");
+ assertFalse(mails.isEmpty());
+ var content = mails.get(mails.size() - 1).htmlMessage().orElseThrow();
+ var path = Paths.get("src/test/resources/mail/" + expectedContentFile);
+ if (OVERWRITE_TEST_FILES) {
+ Files.write(path, content.getBytes(),
+ StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
+ } else {
+ var expectedContent = Files.readString(path);
+ assertEquals(expectedContent, content);
+ }
+ }
+
private void registerTenant(String tenantName, String plan, Duration timeSinceLastLogin) {
var name = TenantName.from(tenantName);
tester.createTenant(tenantName, Tenant.Type.cloud);
@@ -111,4 +175,11 @@ public class CloudTrialExpirerTest {
assertEquals(planId, tester.serviceRegistry().billingController().getPlan(TenantName.from(tenant)).value());
}
+ private String lastAccountLevelNotificationTitle(TenantName tenant) {
+ return tester.controller().notificationsDb()
+ .listNotifications(NotificationSource.from(tenant), false).stream()
+ .filter(n -> n.type() == Notification.Type.account).map(Notification::title)
+ .findFirst().orElseThrow();
+ }
+
}