defmodule DaProductApp.RiskManagementTest do use DaProductApp.DataCase alias DaProductApp.RiskManagement alias DaProductApp.RiskManagement.{RiskRule, RiskRuleHit} alias DaProductApp.Transactions.Transaction describe "risk_rules" do @valid_rule_attrs %{ name: "Test High Value Rule", description: "Test rule for high value transactions", category: "Cat A", rule_type: "hold", enabled: true, execution_order: 1, parameters: %{"threshold" => 10000} } @invalid_rule_attrs %{name: nil, category: nil, rule_type: nil} def risk_rule_fixture(attrs \\ %{}) do {:ok, risk_rule} = attrs |> Enum.into(@valid_rule_attrs) |> RiskManagement.create_risk_rule() risk_rule end test "list_risk_rules/1 returns all risk rules" do risk_rule = risk_rule_fixture() assert RiskManagement.list_risk_rules() |> Enum.map(&(&1.id)) |> Enum.member?(risk_rule.id) end test "list_risk_rules/1 filters by category" do _cat_a_rule = risk_rule_fixture(%{category: "Cat A", name: "Cat A Rule"}) cat_b_rule = risk_rule_fixture(%{category: "Cat B", name: "Cat B Rule"}) cat_b_rules = RiskManagement.list_risk_rules(category: "Cat B") assert length(cat_b_rules) >= 1 assert Enum.any?(cat_b_rules, &(&1.id == cat_b_rule.id)) end test "get_risk_rule!/1 returns the risk rule with given id" do risk_rule = risk_rule_fixture() assert RiskManagement.get_risk_rule!(risk_rule.id).id == risk_rule.id end test "create_risk_rule/1 with valid data creates a risk rule" do assert {:ok, %RiskRule{} = risk_rule} = RiskManagement.create_risk_rule(@valid_rule_attrs) assert risk_rule.name == "Test High Value Rule" assert risk_rule.category == "Cat A" assert risk_rule.rule_type == "hold" assert risk_rule.enabled == true end test "create_risk_rule/1 with invalid data returns error changeset" do assert {:error, %Ecto.Changeset{}} = RiskManagement.create_risk_rule(@invalid_rule_attrs) end test "update_risk_rule/2 with valid data updates the risk rule" do risk_rule = risk_rule_fixture() update_attrs = %{name: "Updated Rule Name", enabled: false} assert {:ok, %RiskRule{} = updated_rule} = RiskManagement.update_risk_rule(risk_rule, update_attrs) assert updated_rule.name == "Updated Rule Name" assert updated_rule.enabled == false end test "delete_risk_rule/1 deletes the risk rule" do risk_rule = risk_rule_fixture() assert {:ok, %RiskRule{}} = RiskManagement.delete_risk_rule(risk_rule) assert_raise Ecto.NoResultsError, fn -> RiskManagement.get_risk_rule!(risk_rule.id) end end end describe "transaction risk evaluation" do test "evaluate_transaction_risks/2 identifies high value transactions" do # Create a high value rule rule = risk_rule_fixture(%{ name: "High Value Transaction", parameters: %{"threshold" => 5000} }) # Create a transaction that should trigger the rule transaction = %Transaction{ id: 1, transaction_amount: Decimal.new("10000.00"), merchant_id: "MERCHANT123", status: "completed" } rule_hits = RiskManagement.evaluate_transaction_risks(transaction, "QR") assert length(rule_hits) >= 1 hit = Enum.find(rule_hits, &(&1.rule_id == rule.id)) assert hit != nil assert hit.status == "Hold" assert hit.transaction_id == 1 assert hit.transaction_type == "QR" end test "evaluate_transaction_risks/2 does not trigger for low value transactions" do # Create a high value rule _rule = risk_rule_fixture(%{ name: "High Value Transaction", parameters: %{"threshold" => 5000} }) # Create a transaction below the threshold transaction = %Transaction{ id: 2, transaction_amount: Decimal.new("1000.00"), merchant_id: "MERCHANT123", status: "completed" } rule_hits = RiskManagement.evaluate_transaction_risks(transaction, "QR") # Should not trigger the high value rule high_value_hits = Enum.filter(rule_hits, &(&1[:metadata]["rule_name"] == "High Value Transaction")) assert length(high_value_hits) == 0 end end describe "risk_rule_hits" do test "create_risk_rule_hit/1 creates a rule hit" do rule = risk_rule_fixture() hit_attrs = %{ transaction_id: 123, transaction_type: "QR", rule_id: rule.id, merchant_id: "MERCHANT123", category: "Cat A", status: "Hold", triggered_at: DateTime.utc_now(), metadata: %{"rule_name" => "Test Rule"} } assert {:ok, %RiskRuleHit{} = hit} = RiskManagement.create_risk_rule_hit(hit_attrs) assert hit.transaction_id == 123 assert hit.transaction_type == "QR" assert hit.rule_id == rule.id assert hit.status == "Hold" end test "update_risk_rule_hit_action/4 updates supervisor action" do rule = risk_rule_fixture() {:ok, hit} = RiskManagement.create_risk_rule_hit(%{ transaction_id: 123, transaction_type: "QR", rule_id: rule.id, triggered_at: DateTime.utc_now() }) supervisor_id = 1 action = "Release" notes = "Verified legitimate transaction" assert {:ok, updated_hit} = RiskManagement.update_risk_rule_hit_action(hit.id, supervisor_id, action, notes) assert updated_hit.supervisor_id == supervisor_id assert updated_hit.action_taken == action assert updated_hit.status == action assert updated_hit.notes == notes assert updated_hit.action_at != nil end end describe "supervisor dashboard" do test "get_new_flagged_transactions/1 returns transactions since last check" do supervisor_id = 1 # Update supervisor's last check to 1 hour ago one_hour_ago = DateTime.add(DateTime.utc_now(), -3600, :second) RiskManagement.update_supervisor_last_checked(supervisor_id) # Insert a supervisor record with 1 hour ago timestamp DaProductApp.Repo.insert!(%DaProductApp.RiskManagement.SupervisorLastChecked{ supervisor_id: supervisor_id, last_checked_at: one_hour_ago }, on_conflict: :replace_all, conflict_target: [:supervisor_id]) # Create a rule and hit that occurred after the last check rule = risk_rule_fixture() {:ok, _hit} = RiskManagement.create_risk_rule_hit(%{ transaction_id: 456, transaction_type: "QR", rule_id: rule.id, triggered_at: DateTime.utc_now() }) new_hits = RiskManagement.get_new_flagged_transactions(supervisor_id) assert length(new_hits) >= 1 end end end