Files
sure/test/models/family_test.rb
soky srm e1ff6d46ee Make categories global (#1160)
* Make categories global

This solves us A LOT of cash flow and budgeting problems.

* Update schema.rb

* Update auto_categorizer.rb

* Update income_statement.rb

* FIX budget sub-categories

* FIX sub-categories and tests

* Add 2 step migration
2026-03-11 15:54:01 +01:00

205 lines
7.2 KiB
Ruby

require "test_helper"
class FamilyTest < ActiveSupport::TestCase
include SyncableInterfaceTest
def setup
@syncable = families(:dylan_family)
end
test "investment_contributions_category creates category when missing" do
family = families(:dylan_family)
family.categories.where(name: Category.investment_contributions_name).destroy_all
assert_nil family.categories.find_by(name: Category.investment_contributions_name)
category = family.investment_contributions_category
assert category.persisted?
assert_equal Category.investment_contributions_name, category.name
assert_equal "#0d9488", category.color
assert_equal "trending-up", category.lucide_icon
end
test "investment_contributions_category returns existing category" do
family = families(:dylan_family)
existing = family.categories.find_or_create_by!(name: Category.investment_contributions_name) do |c|
c.color = "#0d9488"
c.lucide_icon = "trending-up"
end
assert_no_difference "Category.count" do
result = family.investment_contributions_category
assert_equal existing, result
end
end
test "investment_contributions_category uses family locale consistently" do
family = families(:dylan_family)
family.update!(locale: "fr")
family.categories.where(name: [ "Investment Contributions", "Contributions aux investissements" ]).destroy_all
# Simulate different request locales (e.g., from Accept-Language header)
# The category should always be created with the family's locale (French)
category_from_english_request = I18n.with_locale(:en) do
family.investment_contributions_category
end
assert_equal "Contributions aux investissements", category_from_english_request.name
# Second request with different locale should find the same category
assert_no_difference "Category.count" do
category_from_dutch_request = I18n.with_locale(:nl) do
family.investment_contributions_category
end
assert_equal category_from_english_request.id, category_from_dutch_request.id
assert_equal "Contributions aux investissements", category_from_dutch_request.name
end
end
test "investment_contributions_category prevents duplicate categories across locales" do
family = families(:dylan_family)
family.update!(locale: "en")
family.categories.where(name: [ "Investment Contributions", "Contributions aux investissements" ]).destroy_all
# Create category under English family locale
english_category = family.investment_contributions_category
assert_equal "Investment Contributions", english_category.name
# Simulate a request with French locale (e.g., from browser Accept-Language)
# Should still return the English category, not create a French one
assert_no_difference "Category.count" do
I18n.with_locale(:fr) do
french_request_category = family.investment_contributions_category
assert_equal english_category.id, french_request_category.id
assert_equal "Investment Contributions", french_request_category.name
end
end
end
test "investment_contributions_category reuses legacy category with wrong locale" do
family = families(:dylan_family)
family.update!(locale: "fr")
family.categories.where(name: [ "Investment Contributions", "Contributions aux investissements" ]).destroy_all
# Simulate legacy: category was created with English name (old bug behavior)
legacy_category = family.categories.create!(
name: "Investment Contributions",
color: "#0d9488",
lucide_icon: "trending-up"
)
# Should find and reuse the legacy category, updating its name to French
assert_no_difference "Category.count" do
result = family.investment_contributions_category
assert_equal legacy_category.id, result.id
assert_equal "Contributions aux investissements", result.name
end
end
test "investment_contributions_category merges multiple locale variants" do
family = families(:dylan_family)
family.update!(locale: "en")
family.categories.where(name: [ "Investment Contributions", "Contributions aux investissements" ]).destroy_all
# Simulate legacy: multiple categories created under different locales
english_category = family.categories.create!(
name: "Investment Contributions",
color: "#0d9488",
lucide_icon: "trending-up"
)
french_category = family.categories.create!(
name: "Contributions aux investissements",
color: "#0d9488",
lucide_icon: "trending-up"
)
# Create transactions pointing to both categories
account = family.accounts.first
txn1 = Transaction.create!(category: english_category)
Entry.create!(
account: account,
entryable: txn1,
amount: 100,
currency: "USD",
date: Date.current,
name: "Test 1"
)
txn2 = Transaction.create!(category: french_category)
Entry.create!(
account: account,
entryable: txn2,
amount: 200,
currency: "USD",
date: Date.current,
name: "Test 2"
)
# Should merge both categories into one, keeping the oldest
assert_difference "Category.count", -1 do
result = family.investment_contributions_category
assert_equal english_category.id, result.id
assert_equal "Investment Contributions", result.name
# Both transactions should now point to the keeper
assert_equal english_category.id, txn1.reload.category_id
assert_equal english_category.id, txn2.reload.category_id
# French category should be deleted
assert_nil Category.find_by(id: french_category.id)
end
end
test "moniker helpers return expected singular and plural labels" do
family = families(:dylan_family)
family.update!(moniker: "Family")
assert_equal "Family", family.moniker_label
assert_equal "Families", family.moniker_label_plural
family.update!(moniker: "Group")
assert_equal "Group", family.moniker_label
assert_equal "Groups", family.moniker_label_plural
end
test "available_merchants includes family merchants without transactions" do
family = families(:dylan_family)
new_merchant = family.merchants.create!(name: "New Test Merchant")
assert_includes family.available_merchants, new_merchant
end
test "upload_document stores provided metadata on family document" do
family = families(:dylan_family)
family.update!(vector_store_id: nil)
adapter = mock("vector_store_adapter")
adapter.expects(:create_store).with(name: "Family #{family.id} Documents").returns(
VectorStore::Response.new(success?: true, data: { id: "vs_test123" }, error: nil)
)
adapter.expects(:upload_file).with(
store_id: "vs_test123",
file_content: "hello",
filename: "notes.txt"
).returns(
VectorStore::Response.new(success?: true, data: { file_id: "file-xyz" }, error: nil)
)
VectorStore::Registry.stubs(:adapter).returns(adapter)
document = family.upload_document(
file_content: "hello",
filename: "notes.txt",
metadata: { "type" => "financial_document" }
)
assert_not_nil document
assert_equal({ "type" => "financial_document" }, document.metadata)
assert_equal "vs_test123", family.reload.vector_store_id
end
end