usenetnews-dynexp2 0.1.2

USENET news server expiry time dynamic tuning (for INN2)
Documentation
// Copyright 2022 Ian Jackson
// SPDX-License-Identifier: GPL-3.0-or-later
// There is NO WARRANTY.

use crate::prelude::*;

const EXAMPLE: &str = r##"
/remember/:7

# :: dynexpire flood      800000
# :: dynexpire highwater 1200000
# :: dynexpire lowwater  1600000
# :: dynexpire flooddrain 4
# :: dynexpire dffield 3
# :: dynexpire mindays 5
# :: dynexpire increase .25
# :: dynexpire reduce 1
# :: dynexpire spool /var/spool/news
# :: dynexpire history /var/log/news/dynexp
# :: dynexpire on
*:A:2:6:120
# :: dynexpire maxdays 120
# :: dynexpire off

# :: dynexpire mindays 2.5
# :: dynexpire maxdays 60
# :: dynexpire increase .125
# :: dynexpire reduce .5
# :: dynexpire flooddrain 2
# :: dynexpire on
alt*:A:1:3:40
uk.jobs*:A:1:3:40
# :: dynexpire mindays 1.25
# :: dynexpire maxdays 30
# :: dynexpire increase .0625
# :: dynexpire reduce .25
# :: dynexpire flooddrain 1
misc.jobs*:A:1:1.5:20
biz.jobs*:A:1:1.5:20
misc.test*:A:1:1.5:20
# :: dynexpire off

misc.jobs.offered:A:1:2:7
control.cancel:A:10:10:10

# :: dynexpire mindays 24
# :: dynexpire maxdays 240
# :: dynexpire increase 0.5
# :: dynexpire reduce 2
# :: dynexpire flooddrain 4

chiark.*:A:never:never:never

# :: dynexpire on
#example*:A:24:96:240
# :: dynexpire off

# :: dynexpire on
chiark.mail*:A:24:40:240
# :: dynexpire off

chiark.mail.linux-rutgers.kernel*:A:25:25:50

# :: dynexpire on
# :: dynexpire mindays 75
chiark.system:A:75:144:240
# :: dynexpire off

"##;

fn t_example() -> Box<dyn io::Read> {
  Box::new(EXAMPLE.as_bytes())
}

fn t_logs_from(free: f64) -> Vec<LogEntry> {
  let mut loaded = Loaded::read_any(t_example(),"X").unwrap();

  let mut log = LogCollection::default();
  let mock = move |_: &_| AOk(free);
  loaded.update_general(&mut log, Some(&mock)).unwrap();

  let log = log.into_by_history_file_map();

  let spool = log.into_iter().next().unwrap();
  assert_eq!(spool.0, "/var/log/news/dynexp");

  spool.1
}

#[test]
fn display_space(){
  let chk = |v, s| assert_eq!( Space(v).to_string(), s );
  chk(        0.  ,         "0" );
  chk(        1.2 ,         "1" );
  chk(       12.  ,        "12" );
  chk(      123.  ,       "123" );
  chk(     1234.  ,     "1_234" );
  chk(    12345.  ,    "12_345" );
  chk(   123456.  ,   "123_456" );
  chk(  1234567.  , "1_234_567" );
}

fn t_params() -> Parameters {
  Parameters {
    history:     "/var/log/news/dynexp".into(),
    flood:       Space(      800000.0,    ),
    highwater:   Space(     1200000.0,    ),
    lowwater:    Space(     1600000.0,    ),
    mindays:     Days(        5.0,    ),
    maxdays:         "      120.0     ".trim().parse().unwrap(),
    flooddrain:  Days(        4.0,    ),
    increase:    Days(        0.25,   ),
    reduce:      Days(        1.0,    ),
  }
}

#[test]
fn lambda() {
  let params = t_params();
  let entry = ActiveData::new(
    params,
    "*:A:2:6:120".parse().unwrap(),
  ).unwrap();
  assert_eq!(
    Lambda( 1. / 115. ),
    entry.cfg_range().mk_lambda(Days(6.0)),
  );
}

#[test]
fn update() {
  let spool = t_logs_from(1500000.);
  {
    let ent = spool.first().unwrap();
    assert_eq!( ent.action.situation, Situation::Ok  );
    assert_eq!( ent.action.action,    "unchanged"    );
    assert_eq!( ent.action.was,       Days(  6.    ) );
    assert_eq!( ent.action.now,       Days(  6.    ) );
  }
  {
    let ent = spool.last().unwrap();
    assert_eq!( ent.action.situation, Situation::Ok  );
    assert_eq!( ent.action.action,    "trimmed"      );
    assert_eq!( ent.action.was,       Days(144.    ) );
    assert_eq!( ent.action.now,       Days(142.    ) );
  }

  let spool = t_logs_from(2000000.);
  {
    let ent = spool.first().unwrap();
    assert_eq!( ent.action.situation, Situation::Low  );
    assert_eq!( ent.action.action,    "increased"     );
    assert_eq!( ent.action.was,       Days(  6.     ) );
    assert_eq!( ent.action.now,       Days(  6.25   ) );
  }
  {
    let ent = spool.last().unwrap();
    assert_eq!( ent.action.situation, Situation::Low );
    assert_eq!( ent.action.action,    "unchanged"    );
    assert_eq!( ent.action.was,       Days(144.    ) );
    assert_eq!( ent.action.now,       Days(144.    ) );
  }

  let spool = t_logs_from(1000000.);
  {
    let ent = spool.first().unwrap();
    assert_eq!( ent.action.situation, Situation::High  );
    assert_eq!( ent.action.action,    "reduced"     );
    assert_eq!( ent.action.was,       Days(  6.     ) );
    assert_eq!( ent.action.now,       Days(  5.     ) );
  }
  {
    let ent = spool.last().unwrap();
    assert_eq!( ent.action.situation, Situation::High );
    assert_eq!( ent.action.action,    "reduced"    );
    assert_eq!( ent.action.was,       Days(144.    ) );
    assert_eq!( ent.action.now,       Days(142.    ) );
  }
}