Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ POSTGRES_USER=local_postgres
POSTGRES_DB=local_db
POSTGRES_PASSWORD=local_password
DATABASE_URL=postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB
PEPPER=a!GB7
PEPPER=a!GB7
EMAIL_SMTP_HOST=localhost
EMAIL_SMTP_PORT=1025
EMAIL_SMTP_USER=
EMAIL_SMTP_PASSWORD=
EMAIL_HTTP_HOST=localhost
EMAIL_HTTP_PORT=1080
6 changes: 6 additions & 0 deletions infra/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ services:
- ../.env.development
ports:
- "5432:5432"
mailcatcher:
container_name: "mailcatcher-dev"
image: "sj26/mailcatcher"
ports:
- "1025:1025"
- "1080:1080"
21 changes: 21 additions & 0 deletions infra/email.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
host: process.env.EMAIL_SMTP_HOST,
port: process.env.EMAIL_SMTP_PORT,
auth: {
user: process.env.EMAIL_SMTP_USER,
pass: process.env.EMAIL_SMTP_PASSWORD,
},
secure: process.env.NODE_ENV === "production",
});

async function send(mailOptions) {
await transporter.sendMail(mailOptions);
}

const email = {
send,
};

export default email;
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"next": "14.2.5",
"next-connect": "1.0.0",
"node-pg-migrate": "7.6.1",
"nodemailer": "7.0.5",
"pg": "8.12.0",
"react": "18.3.1",
"react-dom": "18.3.1",
Expand Down
32 changes: 32 additions & 0 deletions test/integration/infra/email.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import email from "infra/email.js";
import orchestrator from "test/orchestrator.js";

beforeAll(async () => {
await orchestrator.waitAllServices();
});

describe("infra/email.js", () => {
test("send()", async () => {
await orchestrator.deleteAllEmails();
await email.send({
from: "INSystem <contato@insystem.com.br>",
to: "contato@curso.dev",
subject: "Subject test",
text: "Body test",
});

await email.send({
from: "INSystem <contato@insystem.com.br>",
to: "contato@curso.dev",
subject: "Last email sent",
text: "Body of the last email",
});

const lastEmail = await orchestrator.getLastEmail();

expect(lastEmail.sender).toBe("<contato@insystem.com.br>");
expect(lastEmail.recipients[0]).toBe("<contato@curso.dev>");
expect(lastEmail.subject).toBe("Last email sent");
expect(lastEmail.text).toBe("Body of the last email\n");
});
});
41 changes: 39 additions & 2 deletions test/orchestrator.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import migrator from "models/migrator";
import session from "models/session";
import user from "models/user";

const emailHttpUrl = `http://${process.env.EMAIL_HTTP_HOST}:${process.env.EMAIL_HTTP_PORT}`;

async function waitAllServices() {
await forWebServices();
await waitForWebServices();
await waitForEmailServices();

async function forWebServices() {
async function waitForWebServices() {
return retry(fetchStatusPage, {
retries: 100,
maxTimeout: 1000,
Expand All @@ -21,6 +24,20 @@ async function waitAllServices() {
}
}
}

async function waitForEmailServices() {
return retry(fetchEmailPage, {
retries: 100,
maxTimeout: 1000,
});

async function fetchEmailPage() {
const response = await fetch(emailHttpUrl);
if (response.status !== 200) {
throw Error();
}
}
}
}

async function clearDatabase() {
Expand All @@ -44,12 +61,32 @@ async function createSession(userId) {
return session.create(userId);
}

async function deleteAllEmails() {
await fetch(`${emailHttpUrl}/messages`, { method: "DELETE" });
}

async function getLastEmail() {
const emailListResponse = await fetch(`${emailHttpUrl}/messages`);
const emailListResponseBody = await emailListResponse.json();
const lastEmailItem = emailListResponseBody.pop();

const emailTextResponse = await fetch(
`${emailHttpUrl}/messages/${lastEmailItem.id}.plain`,
);
const emailTextBody = await emailTextResponse.text();

lastEmailItem.text = emailTextBody;
return lastEmailItem;
}

const orchestrator = {
waitAllServices,
clearDatabase,
runPendingMigrations,
createUser,
createSession,
deleteAllEmails,
getLastEmail,
};

export default orchestrator;