tests/testthat/test-engine-v3.R

test_that("engine v3 projects lease operations with vacancy, free rent and leasing fees", {
  cfg <- dcf_spec_template()
  cfg$purchase_year <- 2025L
  cfg$horizon_years <- 2L
  cfg$index_rate <- 0
  cfg$opex_inflation_rate <- 0
  cfg$capex_inflation_rate <- 0
  cfg$opex_sqm <- 10
  cfg$landlord_base_opex_sqm <- 5
  cfg$leasing_cost_pct <- 0.10
  cfg$leases <- list(
    list(
      unit = "U1",
      area = 100,
      events = list(
        list(
          start = 2025L,
          end = 2025L,
          rent = 100,
          vac = 0.20,
          free_months = 0,
          capex_sqm = 10,
          new_lease = 0
        ),
        list(
          start = 2026L,
          end = 2026L,
          rent = 120,
          vac = 0,
          free_months = 6,
          capex_sqm = 20,
          new_lease = 1
        )
      )
    )
  )

  ops <- .engine_project_operations(cfg)

  expect_equal(ops$mode, "lease_events")
  expect_equal(ops$noi_price_base, 10000, tolerance = 1e-10)
  expect_equal(ops$noi_vec, c(8000, 10800), tolerance = 1e-10)
  expect_equal(ops$opex_vec, c(700, 500), tolerance = 1e-10)
  expect_equal(ops$capex_vec, c(1000, 2240), tolerance = 1e-10)
})

test_that("engine v3 applies indexation and inflation after base operations are built", {
  cfg <- dcf_spec_template()
  cfg$purchase_year <- 2025L
  cfg$horizon_years <- 3L
  cfg$index_rate <- 0.02
  cfg$opex_inflation_rate <- 0.03
  cfg$capex_inflation_rate <- 0.04
  cfg$opex_sqm <- 0
  cfg$landlord_base_opex_sqm <- 5
  cfg$leases <- list(
    list(
      unit = "U1",
      area = 100,
      events = list(
        list(
          start = 2025L,
          end = 2027L,
          rent = 100,
          vac = 0,
          free_months = 0,
          capex_sqm = 30,
          new_lease = 0
        )
      )
    )
  )

  ops <- .engine_project_operations(cfg)

  expect_equal(ops$noi_vec, c(10000, 10200, 10404), tolerance = 1e-10)
  expect_equal(ops$opex_vec, c(500, 515, 530.45), tolerance = 1e-10)
  expect_equal(ops$capex_vec, c(1000, 1040, 1081.6), tolerance = 1e-10)
})

test_that("cfg_normalize stays a compatibility facade over the v3 internal pipeline", {
  cfg <- dcf_spec_template()
  cfg$purchase_year <- 2025L
  cfg$horizon_years <- 3L
  cfg$acq_price_ht <- 8e6
  cfg$leases <- list(
    list(
      unit = "U1",
      area = 1000,
      events = list(
        list(
          start = 2025L,
          end = 2027L,
          rent = 200,
          vac = 0.05,
          free_months = 0,
          capex_sqm = 5,
          new_lease = 0
        )
      )
    )
  )

  market_inputs <- .engine_normalize_capital_market_inputs(cfg)
  operations <- .engine_project_operations(cfg)
  pricing <- .engine_resolve_pricing(cfg, operations, market_inputs)
  norm_expected <- .engine_flatten_normalized_case(market_inputs, operations, pricing)
  norm_actual <- cfg_normalize(cfg)

  expect_equal(norm_actual, norm_expected, tolerance = 1e-10)
})

test_that("run_case and run_from_config stay aligned on the standard preset YAML", {
  cfg_path <- system.file("extdata", "preset_default.yml", package = "cre.dcf")
  testthat::skip_if(cfg_path == "" || !file.exists(cfg_path), "Config d'exemple introuvable")

  cfg <- dcf_read_config(cfg_path)
  run_case_res <- run_case(config = cfg, ltv_base = "price_di")
  run_from_cfg_res <- run_from_config(cfg_path, ltv_base = "price_di")

  expect_equal(run_case_res$all_equity$irr_project, run_from_cfg_res$dcf$irr_project, tolerance = 1e-10)
  expect_equal(run_case_res$cashflows$free_cash_flow, run_from_cfg_res$ratios$free_cash_flow)
  expect_equal(run_case_res$cashflows$sale_proceeds, run_from_cfg_res$ratios$sale_proceeds)
})

test_that("run_case and run_from_config remain numerically aligned on a lease-driven case", {
  cfg <- dcf_spec_template()
  cfg$purchase_year <- 2025L
  cfg$horizon_years <- 4L
  cfg$arrangement_fee_pct <- 0.01
  cfg$capitalized_fees <- FALSE
  cfg$leases <- list(
    list(
      unit = "U1",
      area = 1200,
      events = list(
        list(
          start = 2025L,
          end = 2028L,
          rent = 210,
          free_months = 0,
          capex_sqm = 0,
          vac = 0.03,
          new_lease = 0
        )
      )
    )
  )

  run_case_res <- run_case(config = cfg, ltv_base = "price_di")
  tmp <- tempfile(fileext = ".yml")
  yaml::write_yaml(cfg, tmp)
  run_from_cfg_res <- run_from_config(tmp, ltv_base = "price_di")

  expect_equal(run_case_res$all_equity$irr_project, run_from_cfg_res$dcf$irr_project, tolerance = 1e-10)
  expect_equal(run_case_res$cashflows$free_cash_flow, run_from_cfg_res$ratios$free_cash_flow)
  expect_equal(run_case_res$cashflows$equity_flow, run_from_cfg_res$ratios$equity_flow)
  expect_equal(run_case_res$cashflows$sale_proceeds, run_from_cfg_res$ratios$sale_proceeds)
})

Try the cre.dcf package in your browser

Any scripts or data that you put into this service are public.

cre.dcf documentation built on April 10, 2026, 5:08 p.m.