27 Commits

Author SHA1 Message Date
baff59181c Update README.md 2018-04-04 23:22:18 +02:00
490d015f80 Update README.md 2018-03-30 15:31:15 +02:00
f1cc2c4ebe Merge pull request #97 from KevinMidboe/coverage
Added coverage stats with nyc and coveralls
2018-03-22 18:30:12 +01:00
2f4421d9e0 Merge branch 'coverage' of github.com:kevinmidboe/seasonedShows into coverage 2018-03-22 18:25:01 +01:00
92cc094787 Removed old coverage script and updated travis config. 2018-03-22 18:24:42 +01:00
f30b46c384 Update README.md 2018-03-22 17:20:14 +01:00
d9f679603a Command in travis config was doc, not cov. 2018-03-22 17:17:23 +01:00
64bd9d1e14 Added support for travis upload test coverage to coveralls. 2018-03-22 15:09:59 +01:00
721826d454 Re-added request and removed superagent. 2018-03-22 13:24:57 +01:00
242fe3515c Removed and moved some dep to dev. 2018-03-22 13:23:30 +01:00
ccf40d2161 Merge pull request #96 from KevinMidboe/snyk-fix-6de5e99f
[Snyk] Fix for 42 vulnerable dependency paths
2018-03-22 12:59:36 +01:00
832b8ba539 Added license and description to package.json. Also updated packages node packages. 2018-03-22 12:56:04 +01:00
snyk-bot
0477e49eca fix: seasoned_api/package.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/npm:qs:20170213
- https://snyk.io/vuln/npm:qs:20140806
- https://snyk.io/vuln/npm:qs:20140806-1
- https://snyk.io/vuln/npm:mime:20170907
- https://snyk.io/vuln/npm:mime:20170907
- https://snyk.io/vuln/npm:fresh:20170908
- https://snyk.io/vuln/npm:debug:20170905
- https://snyk.io/vuln/npm:ms:20170412
- https://snyk.io/vuln/npm:qs:20170213
- https://snyk.io/vuln/npm:negotiator:20160616
- https://snyk.io/vuln/npm:ms:20151024
- https://snyk.io/vuln/npm:debug:20170905
- https://snyk.io/vuln/npm:ms:20170412
- https://snyk.io/vuln/npm:ms:20151024
- https://snyk.io/vuln/npm:hoek:20180212

Latest report for kevinmidboe/seasonedshows:seasoned_api/package.json:
https://snyk.io/test/github/kevinmidboe/seasonedshows?targetFile=seasoned_api/package.json
2018-03-22 11:34:42 +00:00
451b67630a Merge pull request #95 from KevinMidboe/docs_testing
Improved testing, more linting and added paging to request/all endpoint
2018-03-21 23:53:29 +01:00
096bbdf085 Merge branch 'master' into docs_testing 2018-03-21 23:51:31 +01:00
e914e4ab45 Added ORDER BY date that was missing in sql stmt 2018-03-21 23:44:59 +01:00
c1461e1f41 Merge pull request #93 from KevinMidboe/travis
Updated travis to pull submodules
2018-03-21 14:43:36 +01:00
91bf2c1e2a Updated travis to pull submodules 2018-03-21 14:30:26 +01:00
da3df383ed Update gitmodule torrent_search url to be https 2018-03-21 14:29:51 +01:00
9816b978d3 Updated demo on readme 2018-03-21 12:13:57 +01:00
8e22b0f6ea Updated fixtures. 2018-03-20 21:18:11 +01:00
18359f442c Mapped results in tmdb now returns the complete json object so not needed to be created before sent. When getting all requested movies and shows it is now possible to only get one page at a time. 2018-03-20 21:17:41 +01:00
42b8b5ea0e Removed a ) 2018-03-20 20:15:28 +01:00
996295b1fe Removed the id parameter, not used. 2018-03-20 20:14:44 +01:00
1b08c8d3d1 Now when running tests we use a separate config file test.json 2018-03-20 20:14:15 +01:00
9e145f7068 When testing we dont have api access so we create cache entries before each request. The cache key changed so this is now updated in the tests and tmdb. 2018-03-20 20:12:49 +01:00
7051edb212 Added unit tests for testing config file for correct values and if not check the env variables. 2018-03-20 19:25:35 +01:00
21 changed files with 758 additions and 97 deletions

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "torrent_search"]
path = torrent_search
url = git@github.com:KevinMidboe/torrent_search.git
url = https://github.com/KevinMidboe/torrent_search.git

View File

@@ -1,9 +1,10 @@
language: node_js
node_js: '8.7.0'
git:
submodules: false
submodules: true
script:
yarn test
- yarn test
- yarn coverage
before_install:
- cd seasoned_api
before_script: yarn

View File

@@ -1,9 +1,12 @@
# 🌶 seasonedShows
[![Build Status](https://travis-ci.org/KevinMidboe/seasonedShows.svg?branch=master)](https://travis-ci.org/KevinMidboe/seasonedShows)
[![Coverage Status](https://coveralls.io/repos/github/KevinMidboe/seasonedShows/badge.svg?branch=coverage)](https://coveralls.io/github/KevinMidboe/seasonedShows?branch=coverage)
[![Dependency Status](https://www.versioneye.com/user/projects/5ac541370fb24f4489396e02/badge.svg)](https://www.versioneye.com/user/projects/5ac541370fb24f4489396e02)
[![Known Vulnerabilities](https://snyk.io/test/github/KevinMidboe/seasonedShows/badge.svg?targetFile=seasoned_api/package.json)](https://snyk.io/test/github/KevinMidboe/seasonedShows?targetFile=seasoned_api/package.json)
[![DUB](https://img.shields.io/dub/l/vibe-d.svg)]()
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
Your customly *seasoned* movie and show requester, downloader and organizer. Demo page can be viewed [here](https://kevinmidboe.com/request)
Your customly *seasoned* movie and show requester, downloader and organizer.
📺 [Demo](https://kevinmidboe.com/request)
## About
The goal of this project is to create a full custom stack that can to everything surround downloading, organizing and notifiyng of new media. From the top down we have a website using [tmdb](https://www.themoviedb.com) api to search for from over 350k movies and 70k tv shows. Using [hjone72](https://github.com/hjone72/PlexAuth) great PHP reverse proxy we can have a secure way of allowing users to login with their plex credentials which limits request capabilites to only users that are authenticated to use your plex library.

View File

@@ -0,0 +1,17 @@
{
"database": {
"host": ":memory:"
},
"webserver": {
"port": 31400
},
"tmdb": {
"apiKey": "bogus-api-key"
},
"raven": {
"DSN": ""
},
"authentication": {
"secret": "secret"
}
}

View File

@@ -1,35 +1,42 @@
{
"name": "seasoned-api",
"main": "webserver/server.js",
"scripts": {
"start": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. node src/webserver/server.js",
"test": "cross-env SEASONED_CONFIG=conf/development.json TESTING=true NODE_PATH=. mocha --recursive test/system",
"coverage": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. istanbul cover -x script/autogenerate-documentation.js --include-all-sources --dir test/.coverage node_modules/mocha/bin/_mocha --recursive test/**/* -- --report lcovonly && cat test/.coverage/lcov.info | coveralls && rm -rf test/.coverage",
"lint": "./node_modules/.bin/eslint src/"
},
"dependencies": {
"bcrypt-nodejs": "^0.0.3",
"body-parser": "~1.0.1",
"cross-env": "^3.1.3",
"express": "~4.11.0",
"jsonwebtoken": "^8.0.1",
"mongoose": "^3.6.13",
"moviedb": "^0.2.10",
"node-cache": "^4.1.1",
"nodemailer": "^4.0.1",
"python-shell": "^0.4.0",
"raven": "^2.2.1",
"request": "^2.81.0",
"request-promise": "^4.2",
"sqlite3": "3.1.13"
},
"devDependencies": {
"eslint": "^4.9.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.8.0",
"istanbul": "^0.4.5",
"mocha": "^3.1.0",
"supertest": "^2.0.1",
"supertest-as-promised": "^4.0.1"
}
"name": "seasoned-api",
"description": "Packages needed to build and commands to run seasoned api node server.",
"license": {
"type": "MIT",
"url": "https://www.opensource.org/licenses/mit-license.php"
},
"main": "webserver/server.js",
"scripts": {
"start": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. node src/webserver/server.js",
"test": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. mocha --recursive test",
"coverage": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. nyc mocha --recursive test && nyc report --reporter=text-lcov | coveralls",
"lint": "./node_modules/.bin/eslint src/"
},
"dependencies": {
"bcrypt-nodejs": "^0.0.3",
"body-parser": "~1.18.2",
"cross-env": "~5.1.4",
"express": "~4.16.0",
"jsonwebtoken": "^8.0.1",
"mongoose": "~5.0.11",
"moviedb": "^0.2.10",
"node-cache": "^4.1.1",
"python-shell": "^0.5.0",
"request": "^2.85.0",
"request-promise": "^4.2",
"sqlite3": "4.0.0"
},
"devDependencies": {
"coveralls": "^3.0.0",
"eslint": "^4.9.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.8.0",
"istanbul": "^0.4.5",
"mocha": "^5.0.4",
"mocha-lcov-reporter": "^1.3.0",
"nyc": "^11.6.0",
"raven": "^2.4.2",
"supertest": "^3.0.0",
"supertest-as-promised": "^4.0.1"
}
}

View File

@@ -28,7 +28,7 @@ class Config {
const field = new Field(this.fields[section][option]);
if (field.value === '') {
const envField = process.env[[section.toUpperCase(), option.toUpperCase()].join('_')];
const envField = process.env[['SEASONED', section.toUpperCase(), option.toUpperCase()].join('_')];
if (envField !== undefined && envField.length !== 0) { return envField; }
}

View File

@@ -1,8 +1,7 @@
const configuration = require('src/config/configuration').getInstance();
const SqliteDatabase = require('src/database/sqliteDatabase');
const host = process.env.TESTING ? ':memory:' : configuration.get('database', 'host');
const database = new SqliteDatabase(host);
const database = new SqliteDatabase(configuration.get('database', 'host'));
/**
* This module establishes a connection to the database
* specified in the confgiuration file. It tries to setup

View File

@@ -14,8 +14,8 @@ class RequestRepository {
this.queries = {
insertRequest: `INSERT INTO requests(id,title,year,poster_path,background_path,requested_by,ip,user_agent,type)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
fetchRequestedItems: 'SELECT * FROM requests ORDER BY date DESC',
fetchRequestedItemsByStatus: 'SELECT * FROM requests WHERE status IS ? AND type LIKE ? ORDER BY date DESC',
fetchRequestedItems: 'SELECT * FROM requests ORDER BY date DESC LIMIT 25 OFFSET ?*25-25',
fetchRequestedItemsByStatus: 'SELECT * FROM requests WHERE status IS ? AND type LIKE ? ORDER BY date DESC LIMIT 25 OFFSET ?*25-25',
updateRequestedById: 'UPDATE requests SET status = ? WHERE id is ? AND type is ?',
checkIfIdRequested: 'SELECT * FROM requests WHERE id IS ? AND type IS ?',
userRequests: 'SELECT * FROM requests WHERE requested_by IS ?',
@@ -58,23 +58,23 @@ class RequestRepository {
* @returns {Promise} If nothing has gone wrong.
*/
sendRequest(identifier, type, ip, user_agent, user) {
return Promise.resolve()
.then(() => tmdb.lookup(identifier, type))
.then((movie) => {
const username = user === undefined ? undefined : user.username;
// Add request to database
return this.database.run(this.queries.insertRequest, [movie.id, movie.title, movie.year, movie.poster_path, movie.background_path, username, ip, user_agent, movie.type]);
});
return Promise.resolve()
.then(() => tmdb.lookup(identifier, type))
.then((movie) => {
const username = user === undefined ? undefined : user.username;
// Add request to database
return this.database.run(this.queries.insertRequest, [movie.id, movie.title, movie.year, movie.poster_path, movie.background_path, username, ip, user_agent, movie.type]);
});
}
fetchRequested(status, type = '%') {
return Promise.resolve()
.then(() => {
if (status === 'requested' || status === 'downloading' || status === 'downloaded') {
return this.database.all(this.queries.fetchRequestedItemsByStatus, [status, type]);
}
return this.database.all(this.queries.fetchRequestedItems);
});
fetchRequested(status, page = '1', type = '%') {
return Promise.resolve()
.then(() => {
if (status === 'requested' || status === 'downloading' || status === 'downloaded')
return this.database.all(this.queries.fetchRequestedItemsByStatus, [status, type, page]);
else
return this.database.all(this.queries.fetchRequestedItems, page);
})
}
userRequests(user) {

View File

@@ -29,6 +29,7 @@ class TMDB {
/**
* Retrieve a specific movie by id from TMDB.
* @param {Number} identifier of the movie you want to retrieve
* @param {String} type filter results by type (default movie).
* @returns {Promise} succeeds if movie was found
*/
lookup(identifier, type = 'movie') {
@@ -36,7 +37,7 @@ class TMDB {
const cacheKey = `${this.cacheTags.info}:${type}:${identifier}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb(this.tmdbMethod('info', type), query))
.catch(() => this.tmdb(TMDB_METHODS['info'][type], query))
.catch(() => { throw new Error('Could not find a movie with that id.'); })
.then(response => this.cache.set(cacheKey, response))
.then((response) => {
@@ -50,70 +51,66 @@ class TMDB {
}
/**
* Retrive list of of items from TMDB matching the query and/or type given.
* @param {queryText, page, type} the page number to specify in the request for discover,
* Retrive search results from TMDB.
* @param {String} text query you want to search for
* @param {Number} page representing pagination of results
* @param {String} type filter results by type (default multi)
* @returns {Promise} dict with query results, current page and total_pages
*/
search(text, page = 1, type = 'multi') {
const query = { query: text, page };
const query = { query: text, page: page };
const cacheKey = `${this.cacheTags.search}:${page}:${type}:${text}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb(this.tmdbMethod('search', type), query))
.catch(() => this.tmdb(TMDB_METHODS['search'][type], query))
.catch(() => { throw new Error('Could not search for movies/shows at tmdb.'); })
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapResults(response))
.catch((error) => { throw new Error(error); })
.then(([mappedResults, pagenumber, totalpages, total_results]) => ({
results: mappedResults, page: pagenumber, total_results, total_pages: totalpages,
}));
}
/**
* Fetches a given list from tmdb.
* @param {listName} List we want to fetch.
* @param {type} The to specify in the request for discover (default 'movie').
* @param {id} When finding similar a id can be added to query
* @param {page} Page number we want to fetch.
* @param {String} listName Name of list
* @param {String} type filter results by type (default movie)
* @param {Number} page representing pagination of results
* @returns {Promise} dict with query results, current page and total_pages
*/
listSearch(listName, type = 'movie', id, page = '1') {
const params = { id, page };
const cacheKey = `${this.cacheTags[listName]}:${type}:${id}:${page}`;
listSearch(listName, type = 'movie', page = '1') {
const query = { page: page }
console.log(query)
const cacheKey = `${this.cacheTags[listName]}:${type}:${page}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb(this.tmdbMethod(listName, type), params))
.catch(() => this.tmdb(TMDB_METHODS[listName][type], query))
.catch(() => { throw new Error('Error fetching list from tmdb.')})
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapResults(response, type))
.catch((error) => { throw new Error(error); })
.then(([mappedResults, pagenumber, totalpages, total_results]) => ({
results: mappedResults, page: pagenumber, total_pages: totalpages, total_results,
}));
}
tmdbMethod(apiMethod, type) {
const method = TMDB_METHODS[apiMethod][type];
if (method !== undefined) return method;
throw new Error('Could not find tmdb api method.');
}
/**
* Maps our response from tmdb api to a movie/show object.
* @param {response} JSON response from tmdb.
* @param {type} The type declared in listSearch.
* @param {String} response from tmdb.
* @param {String} The type declared in listSearch.
* @returns {Promise} dict with tmdb results, mapped as movie/show objects.
*/
mapResults(response, type) {
console.log(response.page)
return Promise.resolve()
.then(() => {
const mappedResults = response.results.filter((element) => {
return (element.media_type === 'movie' || element.media_type === 'tv' || element.media_type === undefined);
}).map((element) => convertTmdbToSeasoned(element, type));
return [mappedResults, response.page, response.total_pages, response.total_results];
return {results: mappedResults, page: response.page, total_pages: response.total_pages, total_results: response.total_results}
})
.catch((error) => { throw new Error(error); });
}
/**
* Wraps moviedb library to support Promises.
* @param {String} method function name in the library
* @param {Object} argument argument to function being called
* @returns {Promise} succeeds if callback succeeds
*/
tmdb(method, argument) {
return new Promise((resolve, reject) => {
const callback = (error, reponse) => {

View File

@@ -10,9 +10,9 @@ const requestRepository = new RequestRepository();
*/
function fetchRequestedController(req, res) {
// const user = req.loggedInUser;
const { status } = req.query;
const { status, page } = req.query;
requestRepository.fetchRequested(status)
requestRepository.fetchRequested(status, page)
.then((requestedItems) => {
res.send({ success: true, results: requestedItems, total_results: requestedItems.length });
})

View File

@@ -13,8 +13,8 @@ const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
*/
function listSearchController(req, res) {
const listname = req.params.listname;
const { type, id, page } = req.query;
tmdb.listSearch(listname, type, id, page)
const { type, page } = req.query;
tmdb.listSearch(listname, type, page)
.then((results) => {
res.send(results);
}).catch((error) => {

View File

@@ -0,0 +1,13 @@
{
"background_path": "/yIZ1xendyqKvY3FGeeUYUd5X9Mm.jpg",
"id": 329865,
"popularity": 26.978601,
"poster_path": "/hLudzvGfpi6JlwUnsNhXwKKg4j.jpg",
"release_status": "Released",
"score": 7.3,
"summary": "Taking place after alien crafts land around the world, an expert linguist is recruited by the military to determine whether they come in peace or are a threat.",
"tagline": "Why are they here?",
"title": "Arrival",
"type": "movie",
"year": 2016
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@ const popularMoviesSuccess = require('test/fixtures/popular-movies-success-respo
describe('As a user I want to get popular movies', () => {
before(() => resetDatabase());
before(() => createCacheEntry('p:movie::1', popularMoviesSuccess));
before(() => createCacheEntry('p:movie:1', popularMoviesSuccess));
it('should return 200 with the information', () =>
request(app)

View File

@@ -7,7 +7,7 @@ const popularShowsSuccess = require('test/fixtures/popular-show-success-response
describe('As a user I want to get popular shows', () => {
before(() => resetDatabase());
before(() => createCacheEntry('p:show::1', popularShowsSuccess));
before(() => createCacheEntry('p:show:1', popularShowsSuccess));
it('should return 200 with the information', () =>
request(app)

View File

@@ -1,14 +1,17 @@
const resetDatabase = require('test/helpers/resetDatabase');
const createCacheEntry = require('test/helpers/createCacheEntry');
const app = require('src/webserver/app');
const request = require('supertest-as-promised');
const createUser = require('test/helpers/createUser');
const createToken = require('test/helpers/createToken');
const infoMovieSuccess = require('test/fixtures/arrival-info-success-response.json');
describe('As a user I want to request a movie', () => {
before(() => {
return resetDatabase()
.then(() => createUser('test_user', 'test@gmail.com', 'password'));
})
before(() => createCacheEntry('i:movie:329865', infoMovieSuccess));
it('should return 200 when item is requested', () =>
request(app)

View File

@@ -6,7 +6,7 @@ const interstellarQuerySuccess = require('test/fixtures/interstellar-query-succe
describe('As an anonymous user I want to search for a movie', () => {
before(() => resetDatabase());
before(() => createCacheEntry('s:1:movie:interstellar', interstellarQuerySuccess));
before(() => createCacheEntry('se:1:multi:interstellar', interstellarQuerySuccess));
it('should return 200 with the search results even if user is not logged in', () =>
request(app)

View File

@@ -0,0 +1,63 @@
const assert = require('assert');
const Config = require('src/config/configuration.js');
describe('Config', () => {
before(() => {
this.backedUpEnvironmentVariables = Object.assign({}, process.env);
this.backedUpConfigFields = Object.assign({}, Config.getInstance().fields);
});
after(() => {
process.env = this.backedUpEnvironmentVariables;
Config.getInstance().fields = this.backedUpConfigFields;
});
it('should retrieve section and option from config file', () => {
Config.getInstance().fields = { 'webserver': { 'port': 1337 } };
assert.equal(Config.getInstance().get('webserver', 'port'), 1337);
});
it('should resolve to environment variables if option is filtered with env', () => {
Config.getInstance().fields = { 'webserver': { 'port': 'env|SEASONED_WEBSERVER_PORT' } };
process.env.SEASONED_WEBSERVER_PORT = '1338';
assert.equal(Config.getInstance().get('webserver', 'port'), 1338);
});
it('raises an exception if the environment variable does not exist', () => {
Config.getInstance().fields = { 'webserver': { 'port': 'env|DOES_NOT_EXIST' } };
process.env.SEASONED_WEBSERVER_PORT = '1338';
assert.throws(() => Config.getInstance().get('webserver', 'port'), /empty/);
});
it('raises an exception if the environment variable is empty', () => {
Config.getInstance().fields = { 'webserver': { 'port': 'env|SEASONED_WEBSERVER_PORT' } };
process.env.SEASONED_WEBSERVER_PORT = '';
assert.throws(() => Config.getInstance().get('webserver', 'port'), /empty/);
});
it('raises an exception if the section does not exist in the file', () => {
Config.getInstance().fields = { 'webserver': { 'port': '1338' } };
assert.throws(() => Config.getInstance().get('woops', 'port'), /does not exist/);
});
it('raises an exception if the option does not exist in the file', () => {
Config.getInstance().fields = { 'webserver': { 'port': '1338' } };
assert.throws(() => Config.getInstance().get('webserver', 'woops'), /does not exist/);
});
it('returns an array if field is an array', () => {
Config.getInstance().fields = { 'bouncer': { 'whitelist': [1, 2, 3] } };
assert.deepEqual(Config.getInstance().get('bouncer', 'whitelist'), [1, 2, 3]);
});
it('decodes field as base64 if base64| is before the variable', () => {
Config.getInstance().fields = { 'webserver': { 'port': 'base64|MTMzOA==' } };
assert.equal(Config.getInstance().get('webserver', 'port'), 1338);
});
it('decodes environment variable as base64 if BASE64= is before the variable', () => {
Config.getInstance().fields = { 'webserver': { 'port': 'env|base64|SEASONED_WEBSERVER_PORT' } };
process.env.SEASONED_WEBSERVER_PORT = 'MTMzOA==';
assert.equal(Config.getInstance().get('webserver', 'port'), 1338);
});
});

View File

@@ -0,0 +1,72 @@
const assert = require('assert');
const Field = require('src/config/field.js');
describe('Field', () => {
it('should return an array if it is an array', () => {
const field = new Field([1, 2, 3]);
assert.deepEqual(field.value, [1, 2, 3]);
});
it('should return the plain value if it is an ordinary field', () => {
const field = new Field('plain value');
assert.equal(field.value, 'plain value');
});
it('should return false if boolean false is field', () => {
const field = new Field(false);
assert.equal(field.value, false);
});
it('should not include any invalid filters', () => {
const field = new Field('invalid-filter|plain value');
assert.equal(field.value, 'plain value');
});
it('should return the decoded value if it is filtered through base64', () => {
const field = new Field('base64|ZW5jb2RlZCB2YWx1ZQ==');
assert.equal(field.value, 'encoded value');
});
it('should not decode the value if it missing the filter', () => {
const field = new Field('ZW5jb2RlZCB2YWx1ZQ==');
assert.equal(field.value, 'ZW5jb2RlZCB2YWx1ZQ==');
});
it('should retrieve the environment variable if env filter is used', () => {
const environmentVariables = { REDIS_URL: 'redis://127.0.0.1:1234' };
const field = new Field('env|REDIS_URL', environmentVariables);
assert.equal(field.value, 'redis://127.0.0.1:1234');
});
it('should return undefined if the environment variable does not exist', () => {
const environmentVariables = { HTTP_PORT: 8080 };
const field = new Field('env|REDIS_URL', environmentVariables);
assert.equal(field.value, undefined);
});
it('should return undefined if the environment variable is an empty string', () => {
const environmentVariables = { REDIS_URL: '' };
const field = new Field('env|REDIS_URL', environmentVariables);
assert.deepEqual(field.value, undefined);
});
describe('Multiple filters', () => {
it('should decode the environment variable if base64 and env filter are used', () => {
const environmentVariables = { REDIS_URL: 'cmVkaXM6Ly9kYWdibGFkZXQubm8vMTIzNA==' };
const field = new Field('env|base64|REDIS_URL', environmentVariables);
assert.equal(field.value, 'redis://dagbladet.no/1234');
});
it('should disregard the order of filters when env and base64 are used', () => {
const environmentVariables = { REDIS_URL: 'cmVkaXM6Ly9kYWdibGFkZXQubm8vMTIzNA==' };
const field = new Field('base64|env|REDIS_URL', environmentVariables);
assert.equal(field.value, 'redis://dagbladet.no/1234');
});
it('should return undefined if both filters are used and env var does not exist', () => {
const environmentVariables = { REDIS_URL: 'cmVkaXM6Ly9kYWdibGFkZXQubm8vMTIzNA==' };
const field = new Field('base64|env|REDIS_LOL', environmentVariables);
assert.equal(field.value, undefined);
});
});
});

View File

@@ -0,0 +1,34 @@
const assert = require('assert');
const Filters = require('src/config/filters.js');
describe('Filters', () => {
it('should extract base64 as filter if it is at start of string followed by pipe', () => {
const filters = new Filters('base64|');
assert.deepEqual(filters.filters, ['base64']);
});
it('should extract base64 and env as filters if both are separated by pipe', () => {
const filters = new Filters('base64|env|');
assert.deepEqual(filters.filters, ['base64', 'env']);
});
it('should not extract any filters if none are present', () => {
const filters = new Filters('base64');
assert.deepEqual(filters.filters, []);
});
it('should strip env filter from the value', () => {
const filters = new Filters('env|HELLO');
assert.deepEqual(filters.removeFiltersFromValue(), 'HELLO');
});
it('should strip env and base64 filter from the value', () => {
const filters = new Filters('env|base64|HELLO');
assert.deepEqual(filters.removeFiltersFromValue(), 'HELLO');
});
it('should strip no filters from the value if there are no filters', () => {
const filters = new Filters('HELLO');
assert.deepEqual(filters.removeFiltersFromValue(), 'HELLO');
});
});