Skip to content

Commit

Permalink
Add Financial Report API: controller, model, route, test, and update …
Browse files Browse the repository at this point in the history
…global setup
  • Loading branch information
urbantech committed Nov 6, 2024
1 parent 2c945ba commit 5a330c5
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 80 deletions.
67 changes: 48 additions & 19 deletions __tests__/financialReportingController.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
const { createFinancialReport } = require('../controllers/financialReportingController');
const FinancialReport = require('../models/financialReport');

const httpMocks = require('node-mocks-http');
const mongoose = require('mongoose');

// Mock the FinancialReport model
// Mock the entire model
jest.mock('../models/financialReport');

describe('createFinancialReport Controller', () => {
Expand All @@ -14,38 +12,69 @@ describe('createFinancialReport Controller', () => {
req = httpMocks.createRequest();
res = httpMocks.createResponse();
next = jest.fn();
jest.clearAllMocks();
});

it('should create a new financial report and return 201 status', async () => {
// Mock request body
req.body = {
// Setup request body with fixed timestamp
const testData = {
ReportID: 'test-id',
Type: 'test-type',
Data: 'test-data',
Timestamp: new Date(),
Type: 'Annual',
Data: { key: 'test-data' },
TotalRevenue: '1000.00',
TotalExpenses: '500.00',
NetIncome: '500.00',
EquitySummary: ['uuid1', 'uuid2'],
Timestamp: new Date().toISOString() // Convert to ISO string format
};

// Mock the save function
FinancialReport.prototype.save = jest.fn().mockResolvedValue(req.body);
req.body = testData;

// Mock the constructor and save method
const mockSave = jest.fn().mockResolvedValue(testData);
const mockInstance = { save: mockSave };
FinancialReport.mockImplementation(() => mockInstance);

// Execute controller
await createFinancialReport(req, res, next);

// Get response data and parse it
const responseData = JSON.parse(res._getData());

// Assertions
expect(FinancialReport).toHaveBeenCalledWith(req.body);
expect(FinancialReport.prototype.save).toHaveBeenCalled();
expect(FinancialReport).toHaveBeenCalledWith(testData);
expect(mockSave).toHaveBeenCalled();
expect(res.statusCode).toBe(201);
expect(res._getJSONData()).toEqual(req.body);
expect(responseData).toEqual(testData); // Compare with the original test data
expect(next).not.toHaveBeenCalled();
});

it('should handle errors and pass them to the error handling middleware', async () => {
const errorMessage = 'Failed to create financial report';
const rejectedPromise = Promise.reject(new Error(errorMessage));
FinancialReport.prototype.save = jest.fn().mockReturnValue(rejectedPromise);
// Setup request body with fixed timestamp
const testData = {
ReportID: 'test-id',
Type: 'Annual',
Data: { key: 'test-data' },
TotalRevenue: '1000.00',
TotalExpenses: '500.00',
NetIncome: '500.00',
EquitySummary: ['uuid1', 'uuid2'],
Timestamp: new Date().toISOString() // Convert to ISO string format
};

req.body = testData;

// Mock constructor and save method to throw error
const error = new Error('Failed to create financial report');
const mockSave = jest.fn().mockRejectedValue(error);
FinancialReport.mockImplementation(() => ({ save: mockSave }));

// Execute controller
await createFinancialReport(req, res, next);

expect(FinancialReport.prototype.save).toHaveBeenCalled();
expect(next).toHaveBeenCalledWith(expect.objectContaining({ message: errorMessage }));
// Assertions
expect(FinancialReport).toHaveBeenCalledWith(testData);
expect(mockSave).toHaveBeenCalled();
expect(next).toHaveBeenCalledWith(error);
});
});
});
81 changes: 30 additions & 51 deletions __tests__/globalSetup.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,38 @@
require('dotenv').config(); // Load the .env file
const mongoose = require("mongoose");
const { Client } = require('pg');
require('dotenv').config(); // Ensure dotenv loads
const mongoose = require('mongoose');

module.exports = async () => {
// Set up MongoDB
const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/opencap_test'; // Fallback to hardcoded URI
async function connectToMongoDB() {
const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/opencap_test';
if (!mongoUri) {
throw new Error('MongoDB URI is not defined.');
}

await mongoose.connect(mongoUri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});

// Drop the MongoDB database to clean up before tests
await mongoose.connection.dropDatabase();

// Set up PostgreSQL for metadata management
const pgClient = new Client({
user: process.env.PG_USER || 'lakehouse_user',
host: process.env.PG_HOST || 'localhost',
database: process.env.PG_DATABASE || 'lakehouse_metadata',
password: process.env.PG_PASSWORD || 'password',
port: process.env.PG_PORT || 5432,
});

try {
await pgClient.connect();
console.log('Connected to PostgreSQL database.');

// Clean up PostgreSQL data before tests (if necessary)
await pgClient.query('TRUNCATE TABLE datasets, dataset_schema, ingestion_logs RESTART IDENTITY CASCADE;');
console.log('PostgreSQL tables truncated.');

} catch (error) {
console.error('Error connecting to PostgreSQL:', error);
throw new Error('PostgreSQL connection failed.');
} finally {
await pgClient.end();
await mongoose.connect(mongoUri, { useNewUrlParser: true, useUnifiedTopology: true });
console.log('Connected to MongoDB for setup.');
}

async function dropDatabaseWithRetry() {
let attempts = 3;
while (attempts > 0) {
try {
await mongoose.connection.dropDatabase();
console.log('MongoDB test database dropped successfully.');
return;
} catch (error) {
if (error.codeName === 'DatabaseDropPending') {
console.log('Database drop pending, retrying...');
attempts -= 1;
await new Promise((resolve) => setTimeout(resolve, 1000));
} else {
throw error;
}
}
}
throw new Error('Failed to drop MongoDB test database.');
}

// Set up MinIO client (mock or real)
process.env.MINIO_ENDPOINT = process.env.MINIO_ENDPOINT || 'localhost';
process.env.MINIO_PORT = process.env.MINIO_PORT || '9000';
process.env.MINIO_ACCESS_KEY = process.env.MINIO_ACCESS_KEY || 'your-access-key';
process.env.MINIO_SECRET_KEY = process.env.MINIO_SECRET_KEY || 'your-secret-key';
console.log('MinIO environment variables set.');

// Set up Airflow (mock or real)
process.env.AIRFLOW_BASE_URL = process.env.AIRFLOW_BASE_URL || 'http://localhost:8080';
process.env.AIRFLOW_USERNAME = process.env.AIRFLOW_USERNAME || 'admin';
process.env.AIRFLOW_PASSWORD = process.env.AIRFLOW_PASSWORD || 'admin_password';
console.log('Airflow environment variables set.');

// Close the MongoDB connection
module.exports = async () => {
await connectToMongoDB();
await dropDatabaseWithRetry();
await mongoose.connection.close();
console.log('MongoDB connection closed after setup.');
};
30 changes: 20 additions & 10 deletions controllers/financialReportingController.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
// Import necessary modules and models
// controllers/financialReportingController.js
const FinancialReport = require("../models/financialReport");

// Controller function to create a new financial report
async function createFinancialReport(req, res, next) {
const createFinancialReport = async (req, res, next) => {
try {
// Extract financial report data from the request body
const { ReportID, Type, Data, Timestamp } = req.body;
const {
ReportID,
Type,
Data,
TotalRevenue,
TotalExpenses,
NetIncome,
EquitySummary,
Timestamp
} = req.body;

// Create a new financial report document
const financialReport = new FinancialReport({
ReportID,
Type,
Data,
Timestamp,
TotalRevenue,
TotalExpenses,
NetIncome,
EquitySummary,
Timestamp
});

// Save the financial report document to the database
Expand All @@ -24,11 +37,8 @@ async function createFinancialReport(req, res, next) {
// Handle errors and pass them to the error handling middleware
next(error);
}
}

// Add more controller functions for other routes related to Financial Reporting Tool here
};

module.exports = {
createFinancialReport,
// Add more controller functions here as needed
};
createFinancialReport
};
46 changes: 46 additions & 0 deletions models/financialReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// models/financialReport.js

const mongoose = require('mongoose');
const { Schema } = mongoose;
const { v4: uuidv4 } = require('uuid');

const FinancialReportSchema = new Schema({
ReportID: {
type: String,
default: uuidv4, // Generate a UUID by default
unique: true,
required: true,
},
Type: {
type: String,
enum: ['Annual', 'Quarterly'], // Enum values
required: true,
},
Data: {
type: Schema.Types.Mixed, // Stores JSON data
required: true,
},
TotalRevenue: {
type: Schema.Types.Decimal128, // Decimal for monetary values
required: true,
},
TotalExpenses: {
type: Schema.Types.Decimal128,
required: true,
},
NetIncome: {
type: Schema.Types.Decimal128,
required: true,
},
EquitySummary: {
type: [String], // Array of UUIDs (shareClassId)
default: [], // Optional field
},
Timestamp: {
type: Date,
default: Date.now,
required: true,
},
});

module.exports = mongoose.model('FinancialReport', FinancialReportSchema);

0 comments on commit 5a330c5

Please sign in to comment.