Compare commits
	
		
			27 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| baff59181c | |||
| 490d015f80 | |||
| f1cc2c4ebe | |||
| 2f4421d9e0 | |||
| 92cc094787 | |||
| f30b46c384 | |||
| d9f679603a | |||
| 64bd9d1e14 | |||
| 721826d454 | |||
| 242fe3515c | |||
| ccf40d2161 | |||
| 832b8ba539 | |||
|  | 0477e49eca | ||
| 451b67630a | |||
| 096bbdf085 | |||
| e914e4ab45 | |||
| c1461e1f41 | |||
| 91bf2c1e2a | |||
| da3df383ed | |||
| 9816b978d3 | |||
| 8e22b0f6ea | |||
| 18359f442c | |||
| 42b8b5ea0e | |||
| 996295b1fe | |||
| 1b08c8d3d1 | |||
| 9e145f7068 | |||
| 7051edb212 | 
							
								
								
									
										2
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -1,9 +1,12 @@ | ||||
| # 🌶 seasonedShows | ||||
| [](https://travis-ci.org/KevinMidboe/seasonedShows) | ||||
| [](https://coveralls.io/github/KevinMidboe/seasonedShows?branch=coverage) | ||||
| [](https://www.versioneye.com/user/projects/5ac541370fb24f4489396e02) | ||||
| [](https://snyk.io/test/github/KevinMidboe/seasonedShows?targetFile=seasoned_api/package.json) | ||||
| []() | ||||
| [](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.  | ||||
|   | ||||
							
								
								
									
										17
									
								
								seasoned_api/conf/test.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								seasoned_api/conf/test.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| { | ||||
| 	"database": { | ||||
| 		"host": ":memory:" | ||||
| 	}, | ||||
| 	"webserver": { | ||||
| 		"port": 31400 | ||||
| 	}, | ||||
| 	"tmdb": { | ||||
| 		"apiKey": "bogus-api-key" | ||||
| 	}, | ||||
| 	"raven": { | ||||
| 		"DSN": "" | ||||
| 	}, | ||||
| 	"authentication": { | ||||
|     		"secret": "secret" | ||||
|     	} | ||||
| } | ||||
| @@ -1,35 +1,42 @@ | ||||
| { | ||||
|   "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/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", | ||||
|     "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.0.1", | ||||
|         "cross-env": "^3.1.3", | ||||
|         "express": "~4.11.0", | ||||
|     "body-parser": "~1.18.2", | ||||
|     "cross-env": "~5.1.4", | ||||
|     "express": "~4.16.0", | ||||
|     "jsonwebtoken": "^8.0.1", | ||||
|         "mongoose": "^3.6.13", | ||||
|     "mongoose": "~5.0.11", | ||||
|     "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", | ||||
|     "python-shell": "^0.5.0", | ||||
|     "request": "^2.85.0", | ||||
|     "request-promise": "^4.2", | ||||
|         "sqlite3": "3.1.13" | ||||
|     "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": "^3.1.0", | ||||
|         "supertest": "^2.0.1", | ||||
|     "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" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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; } | ||||
|       } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 ?', | ||||
| @@ -67,14 +67,14 @@ class RequestRepository { | ||||
|       }); | ||||
|    } | ||||
|  | ||||
|    fetchRequested(status, type = '%') { | ||||
|    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]); | ||||
|             } | ||||
|             return this.database.all(this.queries.fetchRequestedItems); | ||||
|          }); | ||||
| 	      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) { | ||||
|   | ||||
| @@ -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) => { | ||||
|   | ||||
| @@ -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 }); | ||||
|       }) | ||||
|   | ||||
| @@ -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) => { | ||||
|   | ||||
							
								
								
									
										13
									
								
								seasoned_api/test/fixtures/arrival-info-success-response.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								seasoned_api/test/fixtures/arrival-info-success-response.json
									
									
									
									
										vendored
									
									
										Normal 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
											
										
									
								
							| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
							
								
								
									
										63
									
								
								seasoned_api/test/unit/config/testConfig.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								seasoned_api/test/unit/config/testConfig.js
									
									
									
									
									
										Normal 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); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										72
									
								
								seasoned_api/test/unit/config/testField.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								seasoned_api/test/unit/config/testField.js
									
									
									
									
									
										Normal 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); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										34
									
								
								seasoned_api/test/unit/config/testFilters.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								seasoned_api/test/unit/config/testFilters.js
									
									
									
									
									
										Normal 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'); | ||||
|   }); | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user