Skip to content

Commit

Permalink
add integration test
Browse files Browse the repository at this point in the history
  • Loading branch information
vahidalizad committed Oct 13, 2024
1 parent 0e0d721 commit 91dd798
Show file tree
Hide file tree
Showing 9 changed files with 18,703 additions and 6,225 deletions.
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"arrowParens": "avoid",
"printWidth": 100
}
24,595 changes: 18,425 additions & 6,170 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"pretest": "npm run lint",
"test": "NODE_ENV=test NODE_CONFIG_DIR=./spec/config istanbul cover -x **/spec/** jasmine --captureExceptions",
"lint": "eslint --cache ./",
"mongo:run": "mongodb-runner start -t replset --version 6.0 -- --port 27017",
"mongo:stop": "mongodb-runner stop --all",
"test:integration": "npm run mongo:run && npm run test && npm run mongo:stop",
"lint:fix": "eslint --fix --cache ./"
},
"repository": {
Expand Down Expand Up @@ -41,8 +44,11 @@
"eslint-plugin-import": "2.22.1",
"istanbul": "0.4.5",
"jasmine": "3.5.0",
"parse": "3.3.1",
"parse": "5.3.0",
"parse-server": "^7.3.0",
"parse-server-conformance-tests": "1.0.0",
"prettier": "3.3.3",
"mongodb-runner": "5.6.4",
"semantic-release": "17.4.6"
}
}
72 changes: 72 additions & 0 deletions spec/integration.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const Parse = require('parse/node');
const { TestUtils } = require('parse-server');
const { PARSE_APP_ID, PARSE_MASTER_KEY, reconfigureServer, serverURL } = require('./mocks/server');
const { httpRequest } = require('./support/request');

const fileData = 'hello world';

describe('S3Adapter integration tests', () => {
beforeEach(async () => {
process.env.TESTING = true;

await reconfigureServer();

Parse.initialize(PARSE_APP_ID);
Parse.serverURL = serverURL;
Parse.CoreManager.set('SERVER_URL', serverURL);
Parse.CoreManager.set('MASTER_KEY', PARSE_MASTER_KEY);
}, 60 * 1000);

afterAll(async () => {
Parse.Storage._clear();
await TestUtils.destroyAllDataPermanently(true);
});

it('should create a file in Parse Server', async () => {
const fileName = 'test-1.txt';

const base64 = Buffer.from(fileData).toString('base64');
const file = new Parse.File(fileName, { base64 });

await file.save();

expect(file).toBeDefined();
expect(file.url()).toContain(fileName);
});

it(
'should read the contents of the file',
async () => {
const fileName = 'test-2.txt';
const base64 = Buffer.from(fileData).toString('base64');
const file = new Parse.File(fileName, { base64 });
await file.save();
const fileLink = file.url();

const response = await httpRequest(fileLink);
const text = response.toString();

expect(text).toBe(fileData); // Check if the contents match the original data
},
60 * 1000
);

it(
'should delete the file',
async () => {
const fileName = 'test-3.txt';

const base64 = Buffer.from(fileData).toString('base64');
const file = new Parse.File(fileName, { base64 });
await file.save();

const fileLink = file.url();
await file.destroy();

return expectAsync(httpRequest(fileLink)).toBeRejectedWithError(
'Request failed with status code 404'
);
},
60 * 1000
);
});
18 changes: 18 additions & 0 deletions spec/mocks/MockEmailAdapterWithOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = (options = {}) => {
const adapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => Promise.resolve(),
};
if (options.sendMail) {
adapter.sendMail = options.sendMail;
}
if (options.sendPasswordResetEmail) {
adapter.sendPasswordResetEmail = options.sendPasswordResetEmail;
}
if (options.sendVerificationEmail) {
adapter.sendVerificationEmail = options.sendVerificationEmail;
}

return adapter;
};
66 changes: 66 additions & 0 deletions spec/mocks/s3adapter-v2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const S3Adapter = require('../../index.js');

function makeS3Adapter(options) {
let s3;

if (
process.env.TEST_S3_ACCESS_KEY &&
process.env.TEST_S3_SECRET_KEY &&
process.env.TEST_S3_BUCKET
) {
// Should be initialized from the env
s3 = new S3Adapter(
Object.assign(
{
accessKey: process.env.TEST_S3_ACCESS_KEY,
secretKey: process.env.TEST_S3_SECRET_KEY,
bucket: process.env.TEST_S3_BUCKET,
},
options
)
);
} else {
const bucket = 'FAKE_BUCKET';

s3 = new S3Adapter('FAKE_ACCESS_KEY', 'FAKE_SECRET_KEY', bucket, options);

const objects = {};

s3._s3Client = {
createBucket: callback => setTimeout(callback, 100),
upload: (params, callback) =>
setTimeout(() => {
const { Key, Body } = params;

objects[Key] = Body;

callback(null, {
Location: `https://${bucket}.s3.amazonaws.com/${Key}`,
});
}, 100),
deleteObject: (params, callback) =>
setTimeout(() => {
const { Key } = params;

delete objects[Key];

callback(null, {});
}, 100),
getObject: (params, callback) =>
setTimeout(() => {
const { Key } = params;

if (objects[Key]) {
callback(null, {
Body: Buffer.from(objects[Key], 'utf8'),
});
} else {
callback(new Error('Not found'));
}
}, 100),
};
}
return s3;
}

module.exports = { makeS3Adapter };
68 changes: 68 additions & 0 deletions spec/mocks/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const { ParseServer } = require('parse-server');
const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
const { makeS3Adapter } = require('./s3adapter-v2');

const port = 1327;
const mountPath = '/api/parse';
const serverURL = 'http://127.0.0.1:1327/api/parse';

const PARSE_APP_ID = 'app-id';
const PARSE_MASTER_KEY = 'master-key';

const S3Adapter = makeS3Adapter();

const defaultConfiguration = {
databaseURI: 'mongodb://127.0.0.1:27017/s3-adapter',
appId: PARSE_APP_ID,
masterKey: PARSE_MASTER_KEY,
serverURL,
liveQuery: {
classNames: [],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
fileUpload: {
enableForPublic: true,
enableForAnonymousUser: true,
enableForAuthenticatedUser: true,
},
revokeSessionOnPasswordReset: false,
allowCustomObjectId: false,
allowClientClassCreation: true,
encodeParseObjectInCloudFunction: true,
masterKeyIps: ['0.0.0.0/0', '0.0.0.0', '::/0', '::'],
emailAdapter: MockEmailAdapterWithOptions(),
port,
mountPath,
filesAdapter: S3Adapter,
};

let parseServer;

const reconfigureServer = async () => {
if (parseServer) {
await parseServer.handleShutdown();
await new Promise(resolve => parseServer.server.close(resolve));
parseServer = undefined;
return reconfigureServer();
}

parseServer = await ParseServer.startApp(defaultConfiguration);
if (parseServer.config.state === 'initialized') {
console.error('Failed to initialize Parse Server');
return reconfigureServer();
}

return parseServer;
};

module.exports = {
reconfigureServer,
S3Adapter,
port,
mountPath,
serverURL,
PARSE_APP_ID,
PARSE_MASTER_KEY,
};
39 changes: 39 additions & 0 deletions spec/support/request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const http = require('http');
const https = require('https');

/**
* Makes an HTTP or HTTPS request.
* @param {string} url - The URL to request.
* @returns {Promise<string>} - A promise that resolves with the response data or rejects with an error.
*/
function httpRequest(url) {
return new Promise((resolve, reject) => {
// Determine the appropriate module to use based on the URL protocol
const client = url.startsWith('https') ? https : http;

// Make the request
client
.get(url, response => {
let data = '';

// Collect the data chunks
response.on('data', chunk => {
data += chunk;
});

// When the response ends, resolve or reject the promise
response.on('end', () => {
if (response.statusCode >= 200 && response.statusCode < 300) {
resolve(data); // Resolve with the collected data
} else {
reject(new Error(`Request failed with status code ${response.statusCode}`));
}
});
})
.on('error', error => {
reject(new Error(`Error making request: ${error.message}`)); // Reject on error
});
});
}

module.exports = { httpRequest };
55 changes: 1 addition & 54 deletions spec/test.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,9 @@ const filesAdapterTests = require('parse-server-conformance-tests').files;
const Parse = require('parse').Parse;
const S3Adapter = require('../index.js');
const optionsFromArguments = require('../lib/optionsFromArguments');
const {makeS3Adapter} = require("./mocks/s3adapter-v2.js");

describe('S3Adapter tests', () => {
function makeS3Adapter(options) {
let s3;

if (
process.env.TEST_S3_ACCESS_KEY
&& process.env.TEST_S3_SECRET_KEY
&& process.env.TEST_S3_BUCKET) {
// Should be initialized from the env
s3 = new S3Adapter(Object.assign({
accessKey: process.env.TEST_S3_ACCESS_KEY,
secretKey: process.env.TEST_S3_SECRET_KEY,
bucket: process.env.TEST_S3_BUCKET,
}, options));
} else {
const bucket = 'FAKE_BUCKET';

s3 = new S3Adapter('FAKE_ACCESS_KEY', 'FAKE_SECRET_KEY', bucket, options);

const objects = {};

s3._s3Client = {
createBucket: (callback) => setTimeout(callback, 100),
upload: (params, callback) => setTimeout(() => {
const { Key, Body } = params;

objects[Key] = Body;

callback(null, {
Location: `https://${bucket}.s3.amazonaws.com/${Key}`,
});
}, 100),
deleteObject: (params, callback) => setTimeout(() => {
const { Key } = params;

delete objects[Key];

callback(null, {});
}, 100),
getObject: (params, callback) => setTimeout(() => {
const { Key } = params;

if (objects[Key]) {
callback(null, {
Body: Buffer.from(objects[Key], 'utf8'),
});
} else {
callback(new Error('Not found'));
}
}, 100),
};
}
return s3;
}

beforeEach(() => {
delete process.env.S3_BUCKET;
delete process.env.S3_REGION;
Expand Down

0 comments on commit 91dd798

Please sign in to comment.