implement testing
This commit is contained in:
@@ -28,6 +28,14 @@ $env:ADMIN_PASSWORD = "secret"
|
||||
$env:DB_URL = "jdbc:postgresql://localhost:5432/linux_distro_roguelite_promotion"
|
||||
$env:DB_USERNAME = "postgres"
|
||||
$env:DB_PASSWORD = "postgres"
|
||||
$env:TOKEN_SECRET = "<registration-token-secret>"
|
||||
$env:SMTP_HOST = "mail.fretux.ch"
|
||||
$env:SMTP_PORT = "587"
|
||||
$env:SMTP_SECURE = "0"
|
||||
$env:SMTP_USER = "no-reply@fretux.ch"
|
||||
$env:SMTP_PASS = "<smtp-password>"
|
||||
$env:MAIL_FROM = "Fretux <no-reply@fretux.ch>"
|
||||
$env:BASE_URL = "localhost:3000"
|
||||
mvn quarkus:dev
|
||||
```
|
||||
|
||||
@@ -67,6 +75,23 @@ Die PostgreSQL-Verbindung wird ueber Umgebungsvariablen konfiguriert:
|
||||
- `DB_USERNAME`, Standard: `postgres`
|
||||
- `DB_PASSWORD`, Standard: `postgres`
|
||||
|
||||
## Mailversand
|
||||
|
||||
`POST /users` verschickt nach erfolgreicher Registrierung eine Bestaetigungsmail. Die E-Mail-Adresse ist Pflicht und kann wegen des eindeutigen Datenbank-Constraints nur einmal verwendet werden. Wenn der Mailversand fehlschlaegt, wird der neu angelegte Nutzer wieder entfernt und die API antwortet mit `502`.
|
||||
|
||||
Der SMTP-Zugang wird ueber Umgebungsvariablen konfiguriert:
|
||||
|
||||
- `TOKEN_SECRET`: Secret fuer den signierten Registrierungslink
|
||||
- `SMTP_HOST`, Standard: `mail.fretux.ch`
|
||||
- `SMTP_PORT`, Standard: `587`
|
||||
- `SMTP_SECURE`, Standard: `false`; fuer Port 587 kann `0` verwendet werden
|
||||
- `SMTP_USER`, Standard: `no-reply@fretux.ch`
|
||||
- `SMTP_PASS`: SMTP-Passwort
|
||||
- `MAIL_FROM`, Standard: `Fretux <no-reply@fretux.ch>`
|
||||
- `BASE_URL`, Standard: `http://localhost:3000`
|
||||
|
||||
In Tests ist `quarkus.mailer.mock=true` gesetzt, damit keine echte E-Mail verschickt wird.
|
||||
|
||||
Das Schema entspricht dem Klassendiagramm:
|
||||
|
||||
- `users`: `id`, `username`, `email`, `newsletter_optin`, `created_at`
|
||||
|
||||
@@ -57,6 +57,10 @@
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-smallrye-openapi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-mailer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit</artifactId>
|
||||
|
||||
@@ -24,10 +24,12 @@ import java.util.Map;
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public class PromotionResource {
|
||||
private final DatabaseService database;
|
||||
private final RegistrationEmailService registrationEmailService;
|
||||
|
||||
@Inject
|
||||
public PromotionResource(DatabaseService database) {
|
||||
public PromotionResource(DatabaseService database, RegistrationEmailService registrationEmailService) {
|
||||
this.database = database;
|
||||
this.registrationEmailService = registrationEmailService;
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -52,6 +54,14 @@ public class PromotionResource {
|
||||
} catch (RuntimeException exception) {
|
||||
throw mapConflict(exception, "username or email already exists.");
|
||||
}
|
||||
|
||||
try {
|
||||
registrationEmailService.sendRegistrationMail(id, username, email);
|
||||
} catch (RuntimeException exception) {
|
||||
database.execute("DELETE FROM users WHERE id = ?", id);
|
||||
throw new ApiException(502, "Registration email could not be sent. Please try again later.");
|
||||
}
|
||||
|
||||
return Response.status(Response.Status.CREATED).entity(userById(id)).build();
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ registerForm.addEventListener("submit", async (event) => {
|
||||
);
|
||||
|
||||
registerForm.reset();
|
||||
setMessage("Pre-registration complete. Use Auth to unlock private game intel.");
|
||||
setMessage("Pre-registration complete. Check your inbox, then use Auth to unlock private game intel.");
|
||||
await refreshStats();
|
||||
} catch (error) {
|
||||
setMessage(error.message, true);
|
||||
|
||||
@@ -7,6 +7,16 @@ quarkus.datasource.jdbc.url=${DB_URL:jdbc:postgresql://localhost:5432/linux_dist
|
||||
quarkus.datasource.jdbc.max-size=8
|
||||
quarkus.log.category."dev.frederik.promotion".level=INFO
|
||||
|
||||
quarkus.mailer.host=${SMTP_HOST:mail.fretux.ch}
|
||||
quarkus.mailer.port=${SMTP_PORT:587}
|
||||
quarkus.mailer.username=${SMTP_USER:no-reply@fretux.ch}
|
||||
quarkus.mailer.password=${SMTP_PASS:}
|
||||
quarkus.mailer.from=${MAIL_FROM:Fretux <no-reply@fretux.ch>}
|
||||
quarkus.mailer.tls=${SMTP_SECURE:false}
|
||||
quarkus.mailer.start-tls=REQUIRED
|
||||
registration.base-url=${BASE_URL:http://localhost:3000}
|
||||
registration.token-secret=${TOKEN_SECRET:}
|
||||
|
||||
auth.admin.username=${ADMIN_USERNAME:admin}
|
||||
auth.admin.password=${ADMIN_PASSWORD:admin}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package dev.frederik.promotion;
|
||||
|
||||
import io.quarkus.mailer.Mail;
|
||||
import io.quarkus.mailer.MockMailbox;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import jakarta.inject.Inject;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
@@ -21,6 +24,14 @@ class PromotionIntegrationTest {
|
||||
@Inject
|
||||
DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
MockMailbox mailbox;
|
||||
|
||||
@BeforeEach
|
||||
void clearMailbox() {
|
||||
mailbox.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformsEndpointReturnsCurrentLaunchTargetsFromIsolatedDatabase() throws Exception {
|
||||
// Given: the Quarkus app started with the isolated test database and seeded launch platforms.
|
||||
@@ -67,6 +78,48 @@ class PromotionIntegrationTest {
|
||||
Map<String, Object> row = findUserInDatabase(username);
|
||||
assertEquals(email, row.get("email"));
|
||||
assertEquals(Boolean.TRUE, row.get("newsletter_optin"));
|
||||
|
||||
List<Mail> sent = mailbox.getMailsSentTo(email);
|
||||
assertEquals(1, sent.size());
|
||||
assertEquals("Kernel Panic pre-registration", sent.get(0).getSubject());
|
||||
assertTrue(sent.get(0).getText().contains("http://localhost:3000/auth.html?registration_token="));
|
||||
}
|
||||
|
||||
@Test
|
||||
void creatingSecondUserWithSameEmailIsRejected() throws Exception {
|
||||
// Given: an email address already belongs to a pre-registered user.
|
||||
String suffix = Long.toString(System.nanoTime());
|
||||
String email = "unique_" + suffix + "@example.com";
|
||||
Map<String, Object> firstPayload = Map.of(
|
||||
"username", "first_" + suffix,
|
||||
"email", email,
|
||||
"newsletter_optin", false);
|
||||
|
||||
given()
|
||||
.contentType("application/json")
|
||||
.body(firstPayload)
|
||||
.when()
|
||||
.post("/users")
|
||||
.then()
|
||||
.statusCode(201);
|
||||
|
||||
// When: another username tries to reuse the same email address.
|
||||
Map<String, Object> secondPayload = Map.of(
|
||||
"username", "second_" + suffix,
|
||||
"email", email,
|
||||
"newsletter_optin", false);
|
||||
|
||||
given()
|
||||
.contentType("application/json")
|
||||
.body(secondPayload)
|
||||
.when()
|
||||
.post("/users")
|
||||
.then()
|
||||
.statusCode(409);
|
||||
|
||||
// Then: the email address is still stored only once and no second mail is sent.
|
||||
assertEquals(1, countUsersByEmail(email));
|
||||
assertEquals(1, mailbox.getMailsSentTo(email).size());
|
||||
}
|
||||
|
||||
private int countCurrentPlatformsInDatabase() throws Exception {
|
||||
@@ -98,4 +151,19 @@ class PromotionIntegrationTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int countUsersByEmail(String email) throws Exception {
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("""
|
||||
SELECT COUNT(*)
|
||||
FROM users
|
||||
WHERE email = ?
|
||||
""")) {
|
||||
statement.setString(1, email);
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
resultSet.next();
|
||||
return resultSet.getInt(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@ quarkus.datasource.password=sa
|
||||
quarkus.datasource.jdbc.url=jdbc:h2:mem:kernel_panic_test;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH;DB_CLOSE_DELAY=-1
|
||||
quarkus.datasource.jdbc.max-size=4
|
||||
|
||||
quarkus.mailer.mock=true
|
||||
quarkus.mailer.from=Fretux <no-reply@fretux.ch>
|
||||
registration.base-url=http://localhost:3000
|
||||
registration.token-secret=test-token-secret
|
||||
|
||||
auth.admin.username=admin
|
||||
auth.admin.password=secret
|
||||
|
||||
|
||||
Reference in New Issue
Block a user