-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from devzero-inc/feature/unit-test-05
#5 AS: refactored backend code, written unit tests for the app
- Loading branch information
Showing
12 changed files
with
5,005 additions
and
1,805 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,14 @@ | ||
from flask import Flask, jsonify, request | ||
import mysql.connector | ||
from mysql.connector import Error | ||
import os | ||
from flask import Flask | ||
from flask_cors import CORS | ||
from dotenv import load_dotenv | ||
from flask_cors import CORS | ||
|
||
app = Flask(__name__) | ||
CORS(app) | ||
import routes | ||
|
||
load_dotenv() | ||
|
||
DATABASE_CONFIG = { | ||
'host': os.getenv('DB_HOST'), | ||
'database': os.getenv('DB_DATABASE'), | ||
'user': os.getenv('DB_USER'), | ||
'password': os.getenv('DB_PASSWORD') | ||
} | ||
|
||
def get_db_connection(): | ||
try: | ||
conn = mysql.connector.connect(**DATABASE_CONFIG) | ||
if conn.is_connected(): | ||
return conn | ||
except Error as e: | ||
print(f"Error connecting to MySQL Database: {e}") | ||
return None | ||
|
||
@app.route('/accounts', methods=['GET']) | ||
def get_accounts(): | ||
conn = get_db_connection() | ||
if conn: | ||
cursor = conn.cursor(dictionary=True) | ||
cursor.execute('SELECT * FROM accounts') | ||
accounts = cursor.fetchall() | ||
cursor.close() | ||
conn.close() | ||
return jsonify({"data": accounts}) | ||
else: | ||
return jsonify({"error": "Database connection failed"}), 500 | ||
app = Flask(__name__) | ||
CORS(app) | ||
|
||
@app.route('/transactions', methods=['GET']) | ||
def get_transactions(): | ||
conn = get_db_connection() | ||
if conn: | ||
cursor = conn.cursor(dictionary=True) | ||
cursor.execute('SELECT * FROM transactions') | ||
transactions = cursor.fetchall() | ||
cursor.close() | ||
conn.close() | ||
return jsonify({"data": transactions}) | ||
else: | ||
return jsonify({"error": "Database connection failed"}), 500 | ||
|
||
@app.route('/transactions', methods=['POST']) | ||
def add_transaction(): | ||
transaction_data = request.json | ||
conn = get_db_connection() | ||
if conn: | ||
cursor = conn.cursor() | ||
cursor.execute( | ||
'INSERT INTO transactions ' | ||
'(account_id, bank_name, date, type, payee, amount, category) ' | ||
'VALUES (%s, %s, %s, %s, %s, %s, %s)', | ||
( | ||
transaction_data['account_id'], | ||
transaction_data['bank_name'], | ||
transaction_data['date'], | ||
transaction_data['type'], | ||
transaction_data['payee'], | ||
transaction_data['amount'], | ||
transaction_data['category'] | ||
) | ||
) | ||
conn.commit() | ||
cursor.close() | ||
conn.close() | ||
return jsonify({"message": "Transaction added successfully"}), 201 | ||
else: | ||
return jsonify({"error": "Database connection failed"}), 500 | ||
app.register_blueprint(routes.api) | ||
|
||
if __name__ == "__main__": | ||
app.run(debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import os | ||
|
||
DATABASE_CONFIG = { | ||
'host': os.getenv('DB_HOST'), | ||
'database': os.getenv('DB_DATABASE'), | ||
'user': os.getenv('DB_USER'), | ||
'password': os.getenv('DB_PASSWORD') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
from db import get_db_connection | ||
|
||
def fetch_all_accounts(): | ||
conn = get_db_connection() | ||
if conn: | ||
cursor = conn.cursor(dictionary=True) | ||
cursor.execute('SELECT * FROM accounts') | ||
accounts = cursor.fetchall() | ||
cursor.close() | ||
conn.close() | ||
return accounts, None | ||
else: | ||
return None, "Database connection failed" | ||
|
||
def fetch_all_transactions(): | ||
conn = get_db_connection() | ||
if conn: | ||
cursor = conn.cursor(dictionary=True) | ||
cursor.execute('SELECT * FROM transactions') | ||
transactions = cursor.fetchall() | ||
cursor.close() | ||
conn.close() | ||
return transactions, None | ||
else: | ||
return None, "Database connection failed" | ||
|
||
def insert_transaction(transaction_data): | ||
conn = get_db_connection() | ||
if conn: | ||
cursor = conn.cursor() | ||
cursor.execute( | ||
'INSERT INTO transactions (account_id, bank_name, date, type, payee, amount, category) ' | ||
'VALUES (%s, %s, %s, %s, %s, %s, %s)', | ||
( | ||
transaction_data['account_id'], | ||
transaction_data['bank_name'], | ||
transaction_data['date'], | ||
transaction_data['type'], | ||
transaction_data['payee'], | ||
transaction_data['amount'], | ||
transaction_data['category'] | ||
) | ||
) | ||
conn.commit() | ||
cursor.close() | ||
conn.close() | ||
return "Transaction added successfully", None | ||
else: | ||
return None, "Database connection failed" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import mysql.connector | ||
from mysql.connector import Error | ||
from config import DATABASE_CONFIG | ||
|
||
def get_db_connection(): | ||
try: | ||
conn = mysql.connector.connect(**DATABASE_CONFIG) | ||
if conn.is_connected(): | ||
print("Connected to MySQL database") | ||
return conn | ||
except Error as e: | ||
print(f"Error connecting to MySQL Database: {e}") | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from flask import Blueprint, jsonify, request | ||
from databaseService import fetch_all_accounts, fetch_all_transactions, insert_transaction | ||
|
||
api = Blueprint('api', __name__) | ||
|
||
@api.route('/health', methods=['GET']) | ||
def health_check(): | ||
return jsonify({"message": "Server is running"}) | ||
|
||
@api.route('/accounts', methods=['GET']) | ||
def get_accounts(): | ||
accounts, error = fetch_all_accounts() | ||
if error: | ||
return jsonify({"error": error}), 500 | ||
else: | ||
return jsonify({"data": accounts}) | ||
|
||
@api.route('/transactions', methods=['GET']) | ||
def get_transactions(): | ||
transactions, error = fetch_all_transactions() | ||
if error: | ||
return jsonify({"error": error}), 500 | ||
else: | ||
return jsonify({"data": transactions}) | ||
|
||
@api.route('/transactions', methods=['POST']) | ||
def add_transaction(): | ||
transaction_data = request.json | ||
message, error = insert_transaction(transaction_data) | ||
if error: | ||
return jsonify({"error": error}), 500 | ||
else: | ||
return jsonify({"message": message}), 201 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import unittest | ||
from unittest.mock import patch | ||
from db import get_db_connection | ||
import mysql.connector | ||
|
||
class TestDatabaseConnection(unittest.TestCase): | ||
|
||
@patch('mysql.connector.connect') | ||
def test_get_db_connection_success(self, mock_connect): | ||
mock_connect.return_value.is_connected.return_value = True | ||
conn = get_db_connection() | ||
self.assertTrue(conn.is_connected()) | ||
|
||
@patch('mysql.connector.connect') | ||
def test_get_db_connection_failure(self, mock_connect): | ||
mock_connect.side_effect = mysql.connector.Error("Connection failed") | ||
conn = get_db_connection() | ||
self.assertIsNone(conn) | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import unittest | ||
from unittest.mock import patch | ||
from app import app | ||
|
||
class TestFlaskRoutes(unittest.TestCase): | ||
def setUp(self): | ||
self.app = app.test_client() | ||
self.app.testing = True | ||
|
||
def test_health_check(self): | ||
response = self.app.get('/health') | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response.json, {"message": "Server is running"}) | ||
|
||
@patch('routes.fetch_all_accounts') | ||
def test_get_accounts(self, mock_fetch_all_accounts): | ||
mock_fetch_all_accounts.return_value = ([ | ||
{"id": 1, "name": "Account 1", "balance": 1000}, | ||
{"id": 2, "name": "Account 2", "balance": 2000}, | ||
], None) | ||
|
||
response = self.app.get('/accounts') | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response.json, {"data": mock_fetch_all_accounts.return_value[0]}) | ||
|
||
@patch('routes.fetch_all_transactions') | ||
def test_get_transactions(self, mock_fetch_all_transactions): | ||
mock_fetch_all_transactions.return_value = ([ | ||
{"id": 1, "account_id": 1, "payee": "Netflix", "amount": -15, "category": "Entertainment"}, | ||
{"id": 2, "account_id": 2, "payee": "Grocery Store", "amount": -100, "category": "Groceries"}, | ||
], None) | ||
|
||
response = self.app.get('/transactions') | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response.json, {"data": mock_fetch_all_transactions.return_value[0]}) | ||
|
||
@patch('routes.insert_transaction') | ||
def test_add_transaction(self, mock_insert_transaction): | ||
mock_insert_transaction.return_value = ("Transaction added successfully", None) | ||
|
||
transaction_data = { | ||
"account_id": "1", | ||
"bank_name": "Test Bank", | ||
"date": "2022-01-01", | ||
"type": "debit", | ||
"payee": "New Payee", | ||
"amount": 50, | ||
"category": "Test Category", | ||
} | ||
|
||
response = self.app.post('/transactions', json=transaction_data) | ||
self.assertEqual(response.status_code, 201) | ||
self.assertEqual(response.json, {"message": "Transaction added successfully"}) | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
module.exports = { | ||
preset: "ts-jest", | ||
testEnvironment: "node", | ||
moduleNameMapper: { | ||
"^@/(.*)$": "<rootDir>/src/$1", | ||
}, | ||
transform: { | ||
"^.+\\.ts$": "ts-jest", | ||
}, | ||
globals: { | ||
"ts-jest": { | ||
useESM: true, | ||
tsconfig: '<rootDir>/tsconfig.spec.json' | ||
}, | ||
}, | ||
extensionsToTreatAsEsm: [".ts"], | ||
testMatch: ["**/__tests__/**/*.ts?(x)", "**/?(*.)+(spec|test).ts?(x)"], | ||
}; |
Oops, something went wrong.