chore: init commit
in worker.../repo/GITFOLDER.zip is the .git folder.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class MonthlyPortfolioSnapshotJobTest < ActiveJob::TestCase
|
||||
setup do
|
||||
@user = create(:student)
|
||||
@portfolio = @user.portfolio
|
||||
@target_date = Date.new(2024, 12, 1)
|
||||
end
|
||||
|
||||
test "creates snapshot calculating cash and stock holdings" do
|
||||
create(:portfolio_transaction, :deposit, portfolio: @portfolio, amount_cents: 50_000)
|
||||
stock = create(:stock, price_cents: 10_000)
|
||||
create(:portfolio_stock, portfolio: @portfolio, stock: stock, shares: 10)
|
||||
|
||||
assert_difference("PortfolioSnapshot.count", 1) do
|
||||
MonthlyPortfolioSnapshotJob.perform_now(@target_date)
|
||||
end
|
||||
|
||||
snapshot = @portfolio.portfolio_snapshots.find_by(date: @target_date)
|
||||
expected_value = @portfolio.calculate_total_value_cents
|
||||
assert_equal expected_value, snapshot.worth_cents
|
||||
end
|
||||
|
||||
test "handles empty portfolio database" do
|
||||
Portfolio.delete_all
|
||||
|
||||
assert_no_difference("PortfolioSnapshot.count") do
|
||||
MonthlyPortfolioSnapshotJob.perform_now(@target_date)
|
||||
end
|
||||
end
|
||||
|
||||
test "skips existing snapshots" do
|
||||
create(:portfolio_snapshot, portfolio: @portfolio, date: @target_date)
|
||||
|
||||
assert_no_difference("PortfolioSnapshot.count") do
|
||||
MonthlyPortfolioSnapshotJob.perform_now(@target_date)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class OrderExecutionJobTest < ActiveJob::TestCase
|
||||
test "with pending orders" do
|
||||
portfolio1 = create(:portfolio)
|
||||
student1 = create(:student, portfolio: portfolio1)
|
||||
create(
|
||||
:portfolio_transaction,
|
||||
:deposit,
|
||||
portfolio: portfolio1,
|
||||
amount_cents: 10_000
|
||||
)
|
||||
portfolio2 = create(:portfolio)
|
||||
student2 = create(:student, portfolio: portfolio2)
|
||||
create(
|
||||
:portfolio_transaction,
|
||||
:deposit,
|
||||
portfolio: portfolio2,
|
||||
amount_cents: 10_000
|
||||
)
|
||||
stock = create(:stock, price_cents: 1_000)
|
||||
order1 = create(:order, :pending, :buy, user: student1, stock:, shares: 1)
|
||||
order2 = create(:order, :pending, :buy, user: student2, stock:, shares: 2)
|
||||
|
||||
ExecuteOrder.expects(:execute).with(order1).once
|
||||
ExecuteOrder.expects(:execute).with(order2).once
|
||||
TransactionFeeProcessor.expects(:execute).once
|
||||
|
||||
OrderExecutionJob.perform_now
|
||||
end
|
||||
|
||||
test "with no pending orders" do
|
||||
ExecuteOrder.expects(:execute).never
|
||||
TransactionFeeProcessor.expects(:execute).never
|
||||
|
||||
OrderExecutionJob.perform_now
|
||||
end
|
||||
|
||||
test "when an error occurs" do
|
||||
portfolio = create(:portfolio)
|
||||
student = create(:student, portfolio:)
|
||||
create(:portfolio_transaction, :deposit, portfolio:, amount_cents: 10_000)
|
||||
stock = create(:stock, price_cents: 1_000)
|
||||
create(:order, :pending, :buy, user: student, stock:, shares: 1)
|
||||
ExecuteOrder.stubs(:execute).raises(StandardError.new("Test error"))
|
||||
|
||||
assert_raises(StandardError) do
|
||||
OrderExecutionJob.perform_now
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class StockAttributeUpdateJobTest < ActiveJob::TestCase
|
||||
test "processes all stocks in the database" do
|
||||
stock1 = create(:stock, ticker: "STOCK1")
|
||||
stock2 = create(:stock, ticker: "STOCK2")
|
||||
stock3 = create(:stock, ticker: "STOCK3")
|
||||
|
||||
Net::HTTP.expects(:get).times(3).returns(
|
||||
{ "Symbol" => "STOCK1", "Name" => "Company 1" }.to_json,
|
||||
{ "Symbol" => "STOCK2", "Name" => "Company 2" }.to_json,
|
||||
{ "Symbol" => "STOCK3", "Name" => "Company 3" }.to_json
|
||||
)
|
||||
|
||||
StockAttributeUpdateJob.perform_now
|
||||
|
||||
stock1.reload
|
||||
stock2.reload
|
||||
stock3.reload
|
||||
|
||||
assert_equal "Company 1", stock1.company_name
|
||||
assert_equal "Company 2", stock2.company_name
|
||||
assert_equal "Company 3", stock3.company_name
|
||||
end
|
||||
|
||||
test "handles stocks with invalid API responses" do
|
||||
stock1 = create(:stock, ticker: "VALID")
|
||||
stock2 = create(:stock, ticker: "INVALID")
|
||||
|
||||
original_name = stock2.company_name
|
||||
|
||||
Net::HTTP.expects(:get).times(2).returns(
|
||||
{ "Symbol" => "VALID", "Name" => "Valid Company" }.to_json,
|
||||
"{}"
|
||||
)
|
||||
|
||||
StockAttributeUpdateJob.perform_now
|
||||
|
||||
stock1.reload
|
||||
stock2.reload
|
||||
|
||||
assert_equal "Valid Company", stock1.company_name
|
||||
assert_equal original_name, stock2.company_name
|
||||
end
|
||||
|
||||
test "skips stocks without ticker" do
|
||||
stock_with_ticker = create(:stock, ticker: "HAS_TICKER")
|
||||
stock_without_ticker = Stock.new(description: "Test stock")
|
||||
stock_without_ticker.save(validate: false)
|
||||
|
||||
Net::HTTP.expects(:get).once.returns(
|
||||
{ "Symbol" => "HAS_TICKER", "Name" => "Has Ticker Company" }.to_json
|
||||
)
|
||||
|
||||
StockAttributeUpdateJob.perform_now
|
||||
|
||||
stock_with_ticker.reload
|
||||
stock_without_ticker.reload
|
||||
|
||||
assert_equal "Has Ticker Company", stock_with_ticker.company_name
|
||||
assert_nil stock_without_ticker.company_name
|
||||
end
|
||||
|
||||
test "handles empty stock table" do
|
||||
Stock.delete_all
|
||||
Net::HTTP.expects(:get).never
|
||||
|
||||
assert_nothing_raised do
|
||||
StockAttributeUpdateJob.perform_now
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,125 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class StockPricesUpdateJobTest < ActiveJob::TestCase
|
||||
STOCK_URL_MATCHER = %r{https://www\.alphavantage\.co/query\?apikey=[^&]+&function=GLOBAL_QUOTE&symbol=[A-Z]+}
|
||||
|
||||
setup do
|
||||
ENV["ALPHA_VANTAGE_API_KEY"] = "test_api_key"
|
||||
@last_trading_date = Date.parse("2024-05-31")
|
||||
|
||||
stub_request(:get, STOCK_URL_MATCHER)
|
||||
.with(
|
||||
headers: {
|
||||
"Accept" => "*/*",
|
||||
"Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
|
||||
"Host" => "www.alphavantage.co",
|
||||
"User-Agent" => "Ruby"
|
||||
}
|
||||
)
|
||||
.to_return(status: 200, body: Rails.root.join("./test/data/global_quote_f.json").open)
|
||||
end
|
||||
|
||||
test "makes API calls" do
|
||||
create(:stock, ticker: "AAPL")
|
||||
create(:stock, ticker: "KO")
|
||||
|
||||
StockPricesUpdateJob.perform_now
|
||||
assert_requested :get, STOCK_URL_MATCHER, times: 2
|
||||
end
|
||||
|
||||
test "updates existing Stock records" do
|
||||
stock1 = create(:stock, ticker: "AAPL", price_cents: 0)
|
||||
stock2 = create(:stock, ticker: "KO", price_cents: 0)
|
||||
|
||||
assert_no_difference("Stock.count") do
|
||||
StockPricesUpdateJob.perform_now
|
||||
end
|
||||
|
||||
stock1.reload
|
||||
stock2.reload
|
||||
assert stock1.price_cents.positive?
|
||||
assert stock2.price_cents.positive?
|
||||
end
|
||||
|
||||
test "sets Stock ticker and price" do
|
||||
test_tickers = %w[AAPL KO DIS]
|
||||
test_tickers.each { |ticker| create(:stock, ticker: ticker, price_cents: 0) }
|
||||
|
||||
StockPricesUpdateJob.perform_now
|
||||
test_tickers.each do |ticker|
|
||||
stock = Stock.find_by(ticker: ticker)
|
||||
assert stock.present?
|
||||
assert stock.price_cents.present?
|
||||
end
|
||||
end
|
||||
|
||||
test "handles empty stock database gracefully" do
|
||||
# Ensure no stocks exist
|
||||
Stock.delete_all
|
||||
|
||||
assert_no_difference("Stock.count") do
|
||||
assert_nothing_raised do
|
||||
StockPricesUpdateJob.perform_now
|
||||
end
|
||||
end
|
||||
|
||||
assert_not_requested :get, STOCK_URL_MATCHER
|
||||
end
|
||||
|
||||
test "stores current price as yesterday price before updating" do
|
||||
initial_price = 12_345
|
||||
stock = create(:stock, ticker: "AAPL", price_cents: initial_price, yesterday_price_cents: nil)
|
||||
|
||||
StockPricesUpdateJob.perform_now
|
||||
|
||||
stock.reload
|
||||
assert_equal initial_price, stock.yesterday_price_cents
|
||||
assert_not_equal initial_price, stock.price_cents
|
||||
end
|
||||
|
||||
test "preserves yesterday price when API call fails" do
|
||||
initial_price = 12_345
|
||||
stock = create(:stock, ticker: "AAPL", price_cents: initial_price, yesterday_price_cents: nil)
|
||||
|
||||
stub_request(:get, STOCK_URL_MATCHER).to_return(status: 200, body: "{}")
|
||||
|
||||
StockPricesUpdateJob.perform_now
|
||||
|
||||
stock.reload
|
||||
assert_equal initial_price, stock.yesterday_price_cents
|
||||
assert_equal initial_price, stock.price_cents
|
||||
end
|
||||
|
||||
test "stores trading day when updating stock price" do
|
||||
stock = create(:stock, ticker: "AAPL", price_cents: 10_000, last_trading_day: nil)
|
||||
|
||||
StockPricesUpdateJob.perform_now
|
||||
|
||||
stock.reload
|
||||
assert_equal @last_trading_date, stock.last_trading_day
|
||||
end
|
||||
|
||||
test "skips update when trading day hasn't changed" do
|
||||
initial_price = 10_000
|
||||
stock = create(:stock, ticker: "AAPL", price_cents: initial_price, last_trading_day: @last_trading_date)
|
||||
|
||||
StockPricesUpdateJob.perform_now
|
||||
|
||||
stock.reload
|
||||
assert_equal initial_price, stock.price_cents
|
||||
assert_equal @last_trading_date, stock.last_trading_day
|
||||
end
|
||||
|
||||
test "updates when trading day is newer" do
|
||||
prev_trading_date = @last_trading_date - 1.day
|
||||
stock = create(:stock, ticker: "AAPL", price_cents: 10_000, last_trading_day: prev_trading_date)
|
||||
|
||||
StockPricesUpdateJob.perform_now
|
||||
|
||||
stock.reload
|
||||
assert_equal @last_trading_date, stock.last_trading_day
|
||||
assert stock.price_cents.positive?
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user