Skip to content

Commit

Permalink
Started with API tests
Browse files Browse the repository at this point in the history
  • Loading branch information
andris9 committed Dec 23, 2024
1 parent 95326f9 commit c6be355
Show file tree
Hide file tree
Showing 9 changed files with 669 additions and 166 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ jobs:
run: |
npm test
env:
EENGINE_REDIS: redis://127.0.0.1:6379/1
EENGINE_REDIS: redis://127.0.0.1:6379/13
44 changes: 42 additions & 2 deletions Gruntfile.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,56 @@
'use strict';

module.exports = function(grunt) {
const config = require('wild-config');

module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
eslint: {
all: ['lib/**/*.js', 'server.js', 'worker.js', 'Gruntfile.js']
},

wait: {
server: {
options: {
delay: 6 * 1000
}
}
},

shell: {
server: {
command: 'node server.js',
options: {
async: true
}
},
flush: {
command: `redis-cli -u "${config.dbs.redis}" flushdb`,
options: {
async: false
}
},
test: {
command: 'node --test test/',
options: {
async: false
}
},
options: {
stdout: data => console.log(data.toString().trim()),
stderr: data => console.log(data.toString().trim()),
failOnError: true
}
}
});

// Load the plugin(s)
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-shell-spawn');
grunt.loadNpmTasks('grunt-wait');

// Tasks
grunt.registerTask('default', ['eslint']);
grunt.registerTask('test', ['shell:flush', 'shell:server', 'wait:server', 'shell:test', 'shell:server:kill']);

grunt.registerTask('default', ['eslint', 'test']);
};
33 changes: 33 additions & 0 deletions config/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@


# JSON formatted settings
settings = '''
{
"webhooksEnabled": false,
"serviceUrl": "http://127.0.0.1:7077",
"ignoreMailCertErrors": true,
"serviceSecret": "a cat"
}
'''

# static access token
# 2aa97ad0456d6624a55d30780aa2ff61bfb7edc6fa00935b40814b271e718660
preparedToken = "hKJpZNlAZWEzODczZjkyMzRmYjNiOGI3MDAwNDMzNzMxMzhjNzMwYzU2ZmQyZjdmYTI3ZGJjZGM2MWQ0YzljMmIyZGY4ZKdjcmVhdGVk1_-UiOcAZ2lLVqZzY29wZXORoSqrZGVzY3JpcHRpb26qdGVzdCB0b2tlbg"


[service]
secret = "secret cat"

[workers]
imap = 1

[log]
raw = true

[dbs]
# redis connection
redis = "redis://127.0.0.1:6379/13"

[api]
host = "0.0.0.0"
port = 7077
148 changes: 90 additions & 58 deletions lib/email-client/base-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ class BaseClient {
return oauthCredentials;
}

async queueMessage(data, meta) {
async queueMessage(data, meta, connectionOptions) {
let accountData = await this.accountObject.loadAccountData();

let gatewayData;
Expand Down Expand Up @@ -671,7 +671,7 @@ class BaseClient {
}

if (!data.mailMerge || !data.mailMerge.length) {
return this.queueMessageEntry(data, meta, licenseInfo);
return this.queueMessageEntry(data, meta, licenseInfo, connectionOptions);
}

let mailMergeList = data.mailMerge;
Expand Down Expand Up @@ -701,7 +701,7 @@ class BaseClient {
messageCopy.render.params = mailMergeEntry.params;
}

messageProcessors.push(this.queueMessageEntry(messageCopy, meta, licenseInfo));
messageProcessors.push(this.queueMessageEntry(messageCopy, meta, licenseInfo, connectionOptions));
}

let response = {
Expand Down Expand Up @@ -742,7 +742,7 @@ class BaseClient {
return true;
}

async prepareRawMessage(data) {
async prepareRawMessage(data, connectionOptions) {
data.disableFileAccess = true;
data.disableUrlAccess = true;
data.boundaryPrefix = MIME_BOUNDARY_PREFIX;
Expand Down Expand Up @@ -796,18 +796,22 @@ class BaseClient {
documentStoreUsed = true;
} else {
let extendedData = data.reference.inline || data.reference.forwardAttachments;
referencedMessage = await this.getMessage(data.reference.message, {
fields: !extendedData
? {
uid: true,
flags: true,
envelope: true,
headers: ['references']
}
: false,
header: extendedData ? true : false,
textType: extendedData ? '*' : false
});
referencedMessage = await this.getMessage(
data.reference.message,
{
fields: !extendedData
? {
uid: true,
flags: true,
envelope: true,
headers: ['references']
}
: false,
header: extendedData ? true : false,
textType: extendedData ? '*' : false
},
connectionOptions
);
}

if (!referencedMessage && !data.reference.ignoreMissing) {
Expand Down Expand Up @@ -935,7 +939,7 @@ class BaseClient {
}

if (attachmentsToDownload && attachmentsToDownload.length) {
this.checkIMAPConnection();
this.checkIMAPConnection(connectionOptions);

this.logger.info({
msg: 'Fetching attachments from the referenced email',
Expand All @@ -954,10 +958,14 @@ class BaseClient {
this.logger.trace({ msg: 'Using cached email content', attachment: attachment.id, size: content.length });
} else {
// fetch from IMAP
content = await this.getAttachmentContent(attachment.id, {
chunkSize: Math.max(DOWNLOAD_CHUNK_SIZE, 2 * 1024 * 1024),
contentOnly: true
});
content = await this.getAttachmentContent(
attachment.id,
{
chunkSize: Math.max(DOWNLOAD_CHUNK_SIZE, 2 * 1024 * 1024),
contentOnly: true
},
connectionOptions
);
}
if (!content) {
// skip missing?
Expand All @@ -978,12 +986,16 @@ class BaseClient {
// resolve referenced attachments
for (let attachment of data.attachments || []) {
if (attachment.reference && !attachment.content) {
this.checkIMAPConnection();
this.checkIMAPConnection(connectionOptions);

let content = await this.getAttachmentContent(attachment.reference, {
chunkSize: Math.max(DOWNLOAD_CHUNK_SIZE, 2 * 1024 * 1024),
contentOnly: true
});
let content = await this.getAttachmentContent(
attachment.reference,
{
chunkSize: Math.max(DOWNLOAD_CHUNK_SIZE, 2 * 1024 * 1024),
contentOnly: true
},
connectionOptions
);
if (!content) {
let error = new Error('Referenced attachment was not found');
error.code = 'ReferenceNotFound';
Expand Down Expand Up @@ -1011,7 +1023,7 @@ class BaseClient {
return { raw, messageId, documentStoreUsed, referencedMessage };
}

async queueMessageEntry(data, meta, licenseInfo) {
async queueMessageEntry(data, meta, licenseInfo, connectionOptions) {
let accountData = await this.accountObject.loadAccountData();

// normal message
Expand Down Expand Up @@ -1169,21 +1181,25 @@ class BaseClient {
}
documentStoreUsed = true;
} else {
this.checkIMAPConnection();
this.checkIMAPConnection(connectionOptions);

let extendedData = data.reference.inline || data.reference.forwardAttachments;
referencedMessage = await this.getMessage(data.reference.message, {
fields: !extendedData
? {
uid: true,
flags: true,
envelope: true,
headers: ['references']
}
: false,
header: extendedData ? true : false,
textType: extendedData ? '*' : false
});
referencedMessage = await this.getMessage(
data.reference.message,
{
fields: !extendedData
? {
uid: true,
flags: true,
envelope: true,
headers: ['references']
}
: false,
header: extendedData ? true : false,
textType: extendedData ? '*' : false
},
connectionOptions
);
}

if (!referencedMessage && !data.reference.ignoreMissing) {
Expand Down Expand Up @@ -1320,7 +1336,7 @@ class BaseClient {
}

if (attachmentsToDownload && attachmentsToDownload.length) {
this.checkIMAPConnection();
this.checkIMAPConnection(connectionOptions);

this.logger.info({
msg: 'Fetching attachments from the referenced email',
Expand All @@ -1339,10 +1355,14 @@ class BaseClient {
this.logger.trace({ msg: 'Using cached email content', attachment: attachment.id, size: content.length });
} else {
// fetch from IMAP
content = await this.getAttachmentContent(attachment.id, {
chunkSize: Math.max(DOWNLOAD_CHUNK_SIZE, 2 * 1024 * 1024),
contentOnly: true
});
content = await this.getAttachmentContent(
attachment.id,
{
chunkSize: Math.max(DOWNLOAD_CHUNK_SIZE, 2 * 1024 * 1024),
contentOnly: true
},
connectionOptions
);
}
if (!content) {
// skip missing?
Expand All @@ -1363,12 +1383,16 @@ class BaseClient {
// resolve referenced attachments
for (let attachment of data.attachments || []) {
if (attachment.reference && !attachment.content) {
this.checkIMAPConnection();
this.checkIMAPConnection(connectionOptions);

let content = await this.getAttachmentContent(attachment.reference, {
chunkSize: Math.max(DOWNLOAD_CHUNK_SIZE, 2 * 1024 * 1024),
contentOnly: true
});
let content = await this.getAttachmentContent(
attachment.reference,
{
chunkSize: Math.max(DOWNLOAD_CHUNK_SIZE, 2 * 1024 * 1024),
contentOnly: true
},
connectionOptions
);

if (!content) {
let error = new Error('Referenced attachment was not found');
Expand Down Expand Up @@ -2500,10 +2524,14 @@ class BaseClient {
shouldCopy = false;
}

let connectionOptions = { allowSecondary: true };

if (shouldCopy) {
// NB! IMAP only
// Upload the message to Sent Mail folder

try {
this.checkIMAPConnection();
this.checkIMAPConnection(connectionOptions);

let sentMailbox =
data.sentMailPath && typeof data.sentMailPath === 'string'
Expand All @@ -2518,7 +2546,7 @@ class BaseClient {
raw = Buffer.from(raw);
}

const connectionClient = await this.getImapConnection(true, 'submitMessage');
const connectionClient = await this.getImapConnection(connectionOptions, 'submitMessage');

await connectionClient.append(sentMailbox.path, raw, ['\\Seen']);

Expand All @@ -2537,12 +2565,16 @@ class BaseClient {
// Add \Answered flag to referenced message if needed
if (reference && reference.update) {
try {
this.checkIMAPConnection();
await this.updateMessage(reference.message, {
flags: {
add: ['\\Answered'].concat(reference.action === 'forward' ? '$Forwarded' : [])
}
});
this.checkIMAPConnection(connectionOptions);
await this.updateMessage(
reference.message,
{
flags: {
add: ['\\Answered'].concat(reference.action === 'forward' ? '$Forwarded' : [])
}
},
connectionOptions
);
} catch (err) {
this.logger.error({ msg: 'Failed to update reference flags', queueId, messageId, reference, err });
}
Expand Down
Loading

0 comments on commit c6be355

Please sign in to comment.