@@ -214,6 +214,138 @@ mod fee_tests {
214214 assert_eq ! ( fee, 1000 ) ;
215215 }
216216
217+ // ========== Strategy selection tests (Issue #1027) ==========
218+
219+ /// Config with a wide clamp range so the strategy formula, not the
220+ /// min/max bounds, drives the result.
221+ fn strategy_config ( method : FeeCalculationMethod , base_fee : u128 ) -> FeeConfig {
222+ FeeConfig {
223+ base_fee,
224+ min_fee : 0 ,
225+ max_fee : u128:: MAX ,
226+ congestion_sensitivity : 100 ,
227+ demand_factor_bp : BasisPoints :: new ( 0 ) ,
228+ calculation_method : method,
229+ last_updated : 0 ,
230+ }
231+ }
232+
233+ fn strategy_context ( congestion_index : u32 , operation : FeeOperation ) -> FeeContext {
234+ FeeContext {
235+ congestion_index,
236+ demand_factor_bp : BasisPoints :: new ( 0 ) ,
237+ operation,
238+ }
239+ }
240+
241+ /// Fixed strategy ignores congestion and demand entirely.
242+ #[ ink:: test]
243+ fn test_fee_calculator_selects_fixed_strategy ( ) {
244+ let config = strategy_config ( FeeCalculationMethod :: Fixed , 2_000 ) ;
245+ let fee = FeeCalculator :: calculate (
246+ & config,
247+ & strategy_context ( 100 , FeeOperation :: RegisterProperty ) ,
248+ ) ;
249+ assert_eq ! ( fee, 2_000 ) ;
250+ }
251+
252+ /// Dynamic strategy scales with congestion: at 0 congestion it returns
253+ /// the base fee, at full congestion it adds the congestion premium.
254+ #[ ink:: test]
255+ fn test_fee_calculator_selects_dynamic_strategy ( ) {
256+ let config = strategy_config ( FeeCalculationMethod :: Dynamic , 1_000 ) ;
257+
258+ // At rest: multiplier is exactly 10_000 bp → base fee.
259+ let at_rest = FeeCalculator :: calculate (
260+ & config,
261+ & strategy_context ( 0 , FeeOperation :: RegisterProperty ) ,
262+ ) ;
263+ assert_eq ! ( at_rest, 1_000 ) ;
264+
265+ // Full congestion with sensitivity 100 adds
266+ // 100 * 100 * (300 - 100) / 10_000 = 200 bp.
267+ let congested = FeeCalculator :: calculate (
268+ & config,
269+ & strategy_context ( 100 , FeeOperation :: RegisterProperty ) ,
270+ ) ;
271+ assert_eq ! ( congested, 1_020 ) ;
272+ }
273+
274+ /// Tiered strategy selects the multiplier from the operation type.
275+ #[ ink:: test]
276+ fn test_fee_calculator_selects_tiered_strategy ( ) {
277+ let config = strategy_config ( FeeCalculationMethod :: Tiered , 1_000 ) ;
278+
279+ assert_eq ! (
280+ FeeCalculator :: calculate( & config, & strategy_context( 0 , FeeOperation :: RegisterProperty ) ) ,
281+ 2_000
282+ ) ;
283+ assert_eq ! (
284+ FeeCalculator :: calculate( & config, & strategy_context( 0 , FeeOperation :: TransferProperty ) ) ,
285+ 1_500
286+ ) ;
287+ assert_eq ! (
288+ FeeCalculator :: calculate( & config, & strategy_context( 0 , FeeOperation :: CreateEscrow ) ) ,
289+ 1_200
290+ ) ;
291+ assert_eq ! (
292+ FeeCalculator :: calculate(
293+ & config,
294+ & strategy_context( 0 , FeeOperation :: PremiumListingBid )
295+ ) ,
296+ 2_500
297+ ) ;
298+ // Operations without a dedicated tier fall back to the 1x tier.
299+ assert_eq ! (
300+ FeeCalculator :: calculate( & config, & strategy_context( 0 , FeeOperation :: OracleUpdate ) ) ,
301+ 1_000
302+ ) ;
303+ }
304+
305+ /// Exponential strategy squares the congestion factor.
306+ #[ ink:: test]
307+ fn test_fee_calculator_selects_exponential_strategy ( ) {
308+ let config = strategy_config ( FeeCalculationMethod :: Exponential , 1_000 ) ;
309+
310+ // No congestion → base fee.
311+ let at_rest = FeeCalculator :: calculate (
312+ & config,
313+ & strategy_context ( 0 , FeeOperation :: RegisterProperty ) ,
314+ ) ;
315+ assert_eq ! ( at_rest, 1_000 ) ;
316+
317+ // At congestion 10: factor = 10 * 10 * 100 / 100 = 100 bp.
318+ let low = FeeCalculator :: calculate (
319+ & config,
320+ & strategy_context ( 10 , FeeOperation :: RegisterProperty ) ,
321+ ) ;
322+ assert_eq ! ( low, 1_010 ) ;
323+
324+ // Squaring makes congestion 100 ten times the 10-unit factor:
325+ // 100 * 100 * 100 / 100 = 10_000 bp → 2x base.
326+ let high = FeeCalculator :: calculate (
327+ & config,
328+ & strategy_context ( 100 , FeeOperation :: RegisterProperty ) ,
329+ ) ;
330+ assert_eq ! ( high, 2_000 ) ;
331+ }
332+
333+ /// Operations without a dedicated config fall back to the default config,
334+ /// so strategy selection uses the default calculation method.
335+ #[ ink:: test]
336+ fn test_get_config_falls_back_to_default_strategy ( ) {
337+ let contract = FeeManager :: new ( 1000 , 100 , 100_000 ) ;
338+
339+ let default = contract. default_config ( ) ;
340+ assert_eq ! ( default . calculation_method, FeeCalculationMethod :: Dynamic ) ;
341+
342+ // No operation-specific config has been set: the fallback must be the
343+ // default config, and the fee must come from the default strategy.
344+ assert_eq ! ( contract. get_config( FeeOperation :: TransferProperty ) , default ) ;
345+ let fee = contract. calculate_fee ( FeeOperation :: TransferProperty ) ;
346+ assert_eq ! ( fee, FeeCalculator :: calculate( & default , & strategy_context( 0 , FeeOperation :: TransferProperty ) ) ) ;
347+ }
348+
217349
218350 // ========== Dynamic fee model tests (Issue #508) ==========
219351
0 commit comments