test: enable concurrency

This commit is contained in:
Zack Schuster 2020-07-26 18:04:23 -07:00 committed by Zack Schuster
parent 0cc9d4f9e5
commit 341dd64972
5 changed files with 293 additions and 582 deletions

View File

@ -54,7 +54,7 @@
"build": "rollup -c rollup.config.ts",
"lint": "eslint *.ts \"+(smtp|test)/*.ts\"",
"tsc": "tsc",
"test": "ava --serial",
"test": "ava",
"test-cjs": "npm run build && npm run test -- --node-arguments='--title=cjs'"
},
"license": "MIT"

View File

@ -1,4 +1,4 @@
import test from 'ava';
import test, { CbExecutionContext } from 'ava';
import { simpleParser } from 'mailparser';
import {
SMTPServer,
@ -32,327 +32,86 @@ function onAuth(
}
}
const port = 2526;
let server: SMTPServer | null = null;
test.afterEach.cb((t) => server?.close(t.end));
test.cb('no authentication (unencrypted) should succeed', (t) => {
let port = 1000;
function send(
t: CbExecutionContext,
{
authMethods = [],
authOptional = false,
secure = false,
}: {
authMethods?: (keyof typeof AUTH_METHODS)[];
authOptional?: boolean;
secure?: boolean;
} = {}
) {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'piglet@gmail.com',
to: 'pooh@gmail.com',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
server = new SMTPServer({
authMethods: [],
authOptional: true,
const server = new SMTPServer({
authMethods,
secure: secure,
hideSTARTTLS: !secure,
authOptional,
onAuth,
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then((mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
})
.finally(t.end);
} as Record<string, unknown>).then((mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
stream.on('end', callback);
},
});
server.listen(port, () => {
new SMTPClient({ port }).send(new Message(msg), (err) => {
if (err) {
throw err;
}
const p = port++;
server.listen(p, () => {
const options = Object.assign(
{ port: p, ssl: secure, authentication: authMethods },
authOptional ? {} : { user: 'pooh', password: 'honey' }
);
new SMTPClient(options).send(new Message(msg), (err) => {
server.close();
t.end(err);
});
});
}
test.cb('no authentication (unencrypted) should succeed', (t) => {
send(t, { authOptional: true });
});
test.cb('no authentication (encrypted) should succeed', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'piglet@gmail.com',
to: 'pooh@gmail.com',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
server = new SMTPServer({
authMethods: [],
authOptional: true,
secure: true,
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then((mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
})
.finally(t.end);
stream.on('end', callback);
},
});
server.listen(port, () => {
new SMTPClient({ port, ssl: true }).send(new Message(msg), (err) => {
if (err) {
throw err;
}
});
});
send(t, { authOptional: true, secure: true });
});
test.cb('PLAIN authentication (unencrypted) should succeed', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'piglet@gmail.com',
to: 'pooh@gmail.com',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
server = new SMTPServer({
authMethods: [AUTH_METHODS.PLAIN],
hideSTARTTLS: true,
onAuth,
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then((mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
})
.finally(t.end);
stream.on('end', callback);
},
});
server.listen(port, () => {
new SMTPClient({
port,
user: 'pooh',
password: 'honey',
authentication: [AUTH_METHODS.PLAIN],
}).send(new Message(msg), (err) => {
if (err) {
throw err;
}
});
});
send(t, { authMethods: [AUTH_METHODS.PLAIN] });
});
test.cb('PLAIN authentication (encrypted) should succeed', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'piglet@gmail.com',
to: 'pooh@gmail.com',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
server = new SMTPServer({
authMethods: [AUTH_METHODS.PLAIN],
secure: true,
onAuth,
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then((mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
})
.finally(t.end);
stream.on('end', callback);
},
});
server.listen(port, () => {
new SMTPClient({
port,
user: 'pooh',
password: 'honey',
authentication: [AUTH_METHODS.PLAIN],
ssl: true,
}).send(new Message(msg), (err) => {
if (err) {
throw err;
}
});
});
send(t, { authMethods: [AUTH_METHODS.PLAIN], secure: true });
});
test.cb('LOGIN authentication (unencrypted) should succeed', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'piglet@gmail.com',
to: 'pooh@gmail.com',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
server = new SMTPServer({
authMethods: [AUTH_METHODS.LOGIN],
hideSTARTTLS: true,
onAuth,
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then((mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
})
.finally(t.end);
stream.on('end', callback);
},
});
server.listen(port, () => {
new SMTPClient({
port,
user: 'pooh',
password: 'honey',
authentication: [AUTH_METHODS.LOGIN],
}).send(new Message(msg), (err) => {
if (err) {
throw err;
}
});
});
send(t, { authMethods: [AUTH_METHODS.LOGIN] });
});
test.cb('LOGIN authentication (encrypted) should succeed', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'piglet@gmail.com',
to: 'pooh@gmail.com',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
server = new SMTPServer({
authMethods: [AUTH_METHODS.LOGIN],
secure: true,
onAuth,
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then((mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
})
.finally(t.end);
stream.on('end', callback);
},
});
server.listen(port, () => {
new SMTPClient({
port,
user: 'pooh',
password: 'honey',
ssl: true,
authentication: [AUTH_METHODS.LOGIN],
}).send(new Message(msg), (err) => {
if (err) {
throw err;
}
});
});
send(t, { authMethods: [AUTH_METHODS.LOGIN], secure: true });
});
test.cb('XOAUTH2 authentication (unencrypted) should succeed', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'piglet@gmail.com',
to: 'pooh@gmail.com',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
server = new SMTPServer({
authMethods: [AUTH_METHODS.XOAUTH2],
hideSTARTTLS: true,
onAuth,
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then((mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
})
.finally(t.end);
stream.on('end', callback);
},
});
server.listen(port, () => {
new SMTPClient({
port,
user: 'pooh',
password: 'honey',
authentication: [AUTH_METHODS.XOAUTH2],
}).send(new Message(msg), (err) => {
if (err) {
throw err;
}
});
});
send(t, { authMethods: [AUTH_METHODS.XOAUTH2] });
});
test.cb('XOAUTH2 authentication (encrypted) should succeed', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'piglet@gmail.com',
to: 'pooh@gmail.com',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
server = new SMTPServer({
authMethods: [AUTH_METHODS.XOAUTH2],
secure: true,
onAuth,
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then((mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
})
.finally(t.end);
stream.on('end', callback);
},
});
server.listen(port, () => {
new SMTPClient({
port,
user: 'pooh',
password: 'honey',
ssl: true,
authentication: [AUTH_METHODS.XOAUTH2],
}).send(new Message(msg), (err) => {
if (err) {
throw err;
}
});
});
send(t, { authMethods: [AUTH_METHODS.XOAUTH2], secure: true });
});

View File

@ -1,4 +1,4 @@
import test from 'ava';
import test, { CbExecutionContext } from 'ava';
import { simpleParser } from 'mailparser';
import { SMTPServer } from 'smtp-server';
@ -6,51 +6,46 @@ import { DEFAULT_TIMEOUT, SMTPClient, Message } from '../email';
type UnPromisify<T> = T extends Promise<infer U> ? U : T;
const port = 2526;
const client = new SMTPClient({
port,
user: 'pooh',
password: 'honey',
ssl: true,
});
const server = new SMTPServer({ secure: true });
let port = 2000;
let greylistPort = 3000;
const send = (
t: CbExecutionContext,
message: Message,
verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void,
done: () => void
verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void
) => {
server.onData = (stream, _session, callback: () => void) => {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then(verify)
.finally(done);
stream.on('end', callback);
};
client.send(message, (err) => {
if (err) {
throw err;
}
});
};
test.before.cb((t) => {
server.listen(port, function () {
server.onAuth = function (auth, _session, callback) {
const server = new SMTPServer({
secure: true,
onAuth(auth, _session, callback) {
if (auth.username === 'pooh' && auth.password === 'honey') {
callback(null, { user: 'pooh' });
} else {
return callback(new Error('invalid user / pass'));
}
};
t.end();
},
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>).then(verify);
stream.on('end', callback);
},
});
});
test.after.cb((t) => server.close(t.end));
const p = port++;
server.listen(p, () => {
new SMTPClient({
port: p,
user: 'pooh',
password: 'honey',
ssl: true,
}).send(message, (err) => {
server.close();
t.end(err);
});
});
};
test.cb('client invokes callback exactly once for invalid connection', (t) => {
t.plan(1);
@ -62,7 +57,7 @@ test.cb('client invokes callback exactly once for invalid connection', (t) => {
text: 'hello world',
};
client.send(new Message(msg), (err) => {
t.not(err, null);
t.true(err instanceof Error);
t.end();
});
});
@ -91,7 +86,7 @@ test('client deduplicates recipients', (t) => {
cc: 'gannon@gmail.com',
bcc: 'gannon@gmail.com',
};
const stack = client.createMessageStack(new Message(msg));
const stack = new SMTPClient({}).createMessageStack(new Message(msg));
t.true(stack.to.length === 1);
t.is(stack.to[0].address, 'gannon@gmail.com');
});
@ -110,7 +105,7 @@ test.cb('client accepts array recipients', (t) => {
msg.valid((isValid) => {
t.true(isValid);
const stack = client.createMessageStack(msg);
const stack = new SMTPClient({}).createMessageStack(msg);
t.is(stack.to.length, 3);
t.deepEqual(
stack.to.map((x) => x.address),
@ -139,7 +134,7 @@ test.cb('client rejects message without `from` header', (t) => {
subject: 'this is a test TEXT message from emailjs',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
client.send(new Message(msg), (err) => {
new SMTPClient({}).send(new Message(msg), (err) => {
t.true(err instanceof Error);
t.is(err?.message, 'Message must have a `from` header');
t.end();
@ -152,7 +147,7 @@ test.cb('client rejects message without `to`, `cc`, or `bcc` header', (t) => {
from: 'piglet@gmail.com',
text: "It is hard to be brave when you're only a Very Small Animal.",
};
client.send(new Message(msg), (err) => {
new SMTPClient({}).send(new Message(msg), (err) => {
t.true(err instanceof Error);
t.is(
err?.message,
@ -170,16 +165,12 @@ test.cb('client allows message with only `cc` recipient header', (t) => {
text: "It is hard to be brave when you're only a Very Small Animal.",
};
send(
new Message(msg),
(mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.cc?.text, msg.cc);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.cc?.text, msg.cc);
});
});
test.cb('client allows message with only `bcc` recipient header', (t) => {
@ -190,16 +181,12 @@ test.cb('client allows message with only `bcc` recipient header', (t) => {
text: "It is hard to be brave when you're only a Very Small Animal.",
};
send(
new Message(msg),
(mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.bcc, undefined);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.bcc, undefined);
});
});
test('client constructor throws if `password` supplied without `user`', (t) => {
@ -215,7 +202,7 @@ test('client constructor throws if `password` supplied without `user`', (t) => {
});
test.cb('client supports greylisting', (t) => {
t.plan(2);
t.plan(3);
const msg = {
subject: 'this is a test TEXT message from emailjs',
@ -224,10 +211,28 @@ test.cb('client supports greylisting', (t) => {
text: "It is hard to be brave when you're only a Very Small Animal.",
};
const { onRcptTo } = server;
server.onRcptTo = (_address, _session, callback) => {
server.onRcptTo = (a, s, cb) => {
const greylistServer = new SMTPServer({
secure: true,
onRcptTo(_address, _session, callback) {
t.pass();
callback();
},
onAuth(auth, _session, callback) {
if (auth.username === 'pooh' && auth.password === 'honey') {
callback(null, { user: 'pooh' });
} else {
return callback(new Error('invalid user / pass'));
}
},
});
const { onRcptTo } = greylistServer;
greylistServer.onRcptTo = (_address, _session, callback) => {
greylistServer.onRcptTo = (a, s, cb) => {
t.pass();
const err = new Error('greylist');
((err as never) as { responseCode: number }).responseCode = 450;
greylistServer.onRcptTo = onRcptTo;
onRcptTo(a, s, cb);
};
@ -236,12 +241,21 @@ test.cb('client supports greylisting', (t) => {
callback(err);
};
client.send(new Message(msg), (err) => {
if (err) {
t.fail();
}
t.pass();
t.end();
const p = greylistPort++;
greylistServer.listen(p, () => {
new SMTPClient({
port: p,
user: 'pooh',
password: 'honey',
ssl: true,
}).send(new Message(msg), (err) => {
greylistServer.close();
if (err) {
t.fail();
}
t.pass();
t.end();
});
});
});
@ -255,13 +269,6 @@ test.cb('client only responds once to greylisting', (t) => {
text: "It is hard to be brave when you're only a Very Small Animal.",
};
const greylistPort = 2527;
const greylistClient = new SMTPClient({
port: greylistPort,
user: 'pooh',
password: 'honey',
ssl: true,
});
const greylistServer = new SMTPServer({
secure: true,
onRcptTo(_address, _session, callback) {
@ -279,8 +286,15 @@ test.cb('client only responds once to greylisting', (t) => {
},
});
greylistServer.listen(greylistPort, () => {
greylistClient.send(new Message(msg), (err) => {
const p = greylistPort++;
greylistServer.listen(p, () => {
new SMTPClient({
port: p,
user: 'pooh',
password: 'honey',
ssl: true,
}).send(new Message(msg), (err) => {
greylistServer.close();
if (err) {
t.pass();
t.end();

View File

@ -1,58 +1,52 @@
import { readFileSync, createReadStream } from 'fs';
import { join } from 'path';
import test from 'ava';
import test, { CbExecutionContext } from 'ava';
import { simpleParser } from 'mailparser';
import { SMTPServer } from 'smtp-server';
import { SMTPClient, Message, MessageAttachment } from '../email';
const port = 2526;
const client = new SMTPClient({
port,
user: 'pooh',
password: 'honey',
ssl: true,
});
const server = new SMTPServer({ secure: true });
type UnPromisify<T> = T extends Promise<infer U> ? U : T;
const send = (
message: Message,
verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void,
done: () => void
) => {
server.onData = (stream, _session, callback: () => void) => {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>)
.then(verify)
.finally(done);
stream.on('end', callback);
};
client.send(message, (err) => {
if (err) {
throw err;
}
});
};
test.before.cb((t) => {
server.listen(port, function () {
server.onAuth = function (auth, _session, callback) {
let port = 4000;
function send(
t: CbExecutionContext,
message: Message,
verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void
) {
const server = new SMTPServer({
secure: true,
onAuth(auth, _session, callback) {
if (auth.username == 'pooh' && auth.password == 'honey') {
callback(null, { user: 'pooh' });
} else {
return callback(new Error('invalid user / pass'));
}
};
t.end();
},
onData(stream, _session, callback: () => void) {
simpleParser(stream, {
skipHtmlToText: true,
skipTextToHtml: true,
skipImageLinks: true,
} as Record<string, unknown>).then(verify);
stream.on('end', callback);
},
});
});
test.after.cb((t) => server.close(t.end));
const p = port++;
server.listen(p, () => {
new SMTPClient({
port: p,
user: 'pooh',
password: 'honey',
ssl: true,
}).send(message, (err) => {
server.close();
t.end(err);
});
});
}
test.cb('simple text message', (t) => {
const msg = {
@ -65,17 +59,13 @@ test.cb('simple text message', (t) => {
'message-id': 'this is a special id',
};
send(
new Message(msg),
(mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
t.is(mail.messageId, '<' + msg['message-id'] + '>');
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
t.is(mail.messageId, '<' + msg['message-id'] + '>');
});
});
test.cb('null text message', (t) => {
@ -87,13 +77,9 @@ test.cb('null text message', (t) => {
'message-id': 'this is a special id',
};
send(
new Message(msg),
(mail) => {
t.is(mail.text, '\n\n\n');
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.text, '\n\n\n');
});
});
test.cb('empty text message', (t) => {
@ -105,13 +91,9 @@ test.cb('empty text message', (t) => {
'message-id': 'this is a special id',
};
send(
new Message(msg),
(mail) => {
t.is(mail.text, '\n\n\n');
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.text, '\n\n\n');
});
});
test.cb('simple unicode text message', (t) => {
@ -122,16 +104,12 @@ test.cb('simple unicode text message', (t) => {
text: 'hello ✓ friend, i hope this message finds you well.',
};
send(
new Message(msg),
(mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('very large text message', (t) => {
@ -143,16 +121,12 @@ test.cb('very large text message', (t) => {
text: readFileSync(join(__dirname, 'attachments/smtp.txt'), 'utf-8'),
};
send(
new Message(msg),
(mail) => {
t.is(mail.text, msg.text.replace(/\r/g, '') + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.text, msg.text.replace(/\r/g, '') + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('very large text data message', (t) => {
@ -173,17 +147,13 @@ test.cb('very large text data message', (t) => {
},
};
send(
new Message(msg),
(mail) => {
t.is(mail.html, text.replace(/\r/g, ''));
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.html, text.replace(/\r/g, ''));
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('html data message', (t) => {
@ -198,17 +168,13 @@ test.cb('html data message', (t) => {
},
};
send(
new Message(msg),
(mail) => {
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('html file message', (t) => {
@ -223,17 +189,13 @@ test.cb('html file message', (t) => {
},
};
send(
new Message(msg),
(mail) => {
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('html with image embed message', (t) => {
@ -257,21 +219,17 @@ test.cb('html with image embed message', (t) => {
},
};
send(
new Message(msg),
(mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
image.toString('base64')
);
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
image.toString('base64')
);
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('html data and attachment message', (t) => {
@ -286,17 +244,13 @@ test.cb('html data and attachment message', (t) => {
] as MessageAttachment[],
};
send(
new Message(msg),
(mail) => {
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('attachment message', (t) => {
@ -313,20 +267,16 @@ test.cb('attachment message', (t) => {
} as MessageAttachment,
};
send(
new Message(msg),
(mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('attachment sent with unicode filename message', (t) => {
@ -343,21 +293,17 @@ test.cb('attachment sent with unicode filename message', (t) => {
} as MessageAttachment,
};
send(
new Message(msg),
(mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(mail.attachments[0].filename, 'smtp-✓-info.pdf');
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(mail.attachments[0].filename, 'smtp-✓-info.pdf');
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('attachments message', (t) => {
@ -382,24 +328,20 @@ test.cb('attachments message', (t) => {
] as MessageAttachment[],
};
send(
new Message(msg),
(mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(
mail.attachments[1].content.toString('base64'),
tar.toString('base64')
);
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(
mail.attachments[1].content.toString('base64'),
tar.toString('base64')
);
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('streams message', (t) => {
@ -429,24 +371,20 @@ test.cb('streams message', (t) => {
stream.pause();
stream2.pause();
send(
new Message(msg),
(mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(
mail.attachments[1].content.toString('base64'),
tar.toString('base64')
);
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
},
t.end
);
send(t, new Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(
mail.attachments[1].content.toString('base64'),
tar.toString('base64')
);
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
t.is(mail.to?.text, msg.to);
});
});
test.cb('message validation fails without `from` header', (t) => {

View File

@ -2,23 +2,23 @@
import test from 'ava';
import { mimeEncode, mimeWordEncode } from '../email';
test('mimeEncode should encode UTF-8', (t) => {
test('mimeEncode should encode UTF-8', async (t) => {
t.is(mimeEncode('tere ÕÄÖÕ'), 'tere =C3=95=C3=84=C3=96=C3=95');
});
test('mimeEncode should encode trailing whitespace', (t) => {
test('mimeEncode should encode trailing whitespace', async (t) => {
t.is(mimeEncode('tere '), 'tere =20');
});
test('mimeEncode should encode non UTF-8', (t) => {
test('mimeEncode should encode non UTF-8', async (t) => {
t.is(mimeEncode(new Uint8Array([0xbd, 0xc5]), 'utf-16be'), '=EB=B7=85');
});
test('mimeWordEncode should encode', (t) => {
test('mimeWordEncode should encode', async (t) => {
t.is('=?UTF-8?Q?See_on_=C3=B5hin_test?=', mimeWordEncode('See on õhin test'));
});
test('mimeWordEncode should QP-encode mime word', (t) => {
test('mimeWordEncode should QP-encode mime word', async (t) => {
t.is(
'=?UTF-8?Q?=E4=AB=B5=E6=9D=A5=E2=B5=B6=E6=87=9E?=',
mimeWordEncode(
@ -29,14 +29,14 @@ test('mimeWordEncode should QP-encode mime word', (t) => {
);
});
test('mimeWordEncode should Base64-encode mime word', (t) => {
test('mimeWordEncode should Base64-encode mime word', async (t) => {
t.is(
mimeWordEncode('Привет и до свидания', 'B'),
'=?UTF-8?B?0J/RgNC40LLQtdGCINC4INC00L4g0YHQstC40LTQsNC90LjRjw==?='
);
});
test('mimeWordEncode should Base64-encode a long mime word', (t) => {
test('mimeWordEncode should Base64-encode a long mime word', async (t) => {
const payload =
'üöß‹€Привет и до свиданияПривет и до свиданияПривет и до свиданияПривет и до свиданияПривет и до свиданияПривет и до свиданияПривет и до свиданияПривет и до свидания';
const expected =