v3 : modules d'ordre et de titre — moteur, données, serveur (L11)
L'ordre des articles de chaque section (cran 2) et le titre du document (cran 4) deviennent des modules : des unités à part, dont les versions sont des propositions votées comme les articles (dépliées, panier, chaîne). - moteur : ld-engine/src/order.rs (qui est dans l'ordre, ordre proposé, complété à la fin quand un article entre ou sort, places redistribuées, nouvel article en fin de section) ; pending : un ordre de moins de deux articles n'est jamais à voter ; - données : migration 0007 (units.kind, module_amendments) ; le seed déclare ses modules ; module_amend.rs les ajoute une fois, gardé et journalisé, aux documents nés sans eux, sans toucher articles ni votes ; - serveur : modules.rs, order.rs, doc_title.rs (portes pour proposer, entrée en vigueur : les articles changent de place, le document change de titre, en et es passent « à revoir ») ; publication des modules ; - reste mineur : la médiane compte les statuts open, in_force, applied, et eux seuls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -310,10 +310,66 @@ async fn a_withdrawn_proposal_leaves_the_usual_participation() {
|
||||
assert_eq!((p["source"].as_str(), p["sample"].as_u64()), (Some("reference"), Some(4)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn the_usual_participation_counts_the_statuses_it_names_and_only_those() {
|
||||
// Relecture of 71819f9: the list is explicit — open, in force, applied. A decision
|
||||
// taken (a change of inertia applied) keeps its voters; a status not named stays out.
|
||||
let (mut first, state) = Client::new().await;
|
||||
let mut people = Vec::new();
|
||||
for name in ["S1", "S2"] {
|
||||
let mut c = first.other();
|
||||
c.login(name).await;
|
||||
people.push(c);
|
||||
}
|
||||
first.login("S1").await;
|
||||
let doc = first.doc("essai").await;
|
||||
let open: Vec<String> = ["I2", "T1", "E4", "E5", "E6", "E8"]
|
||||
.iter()
|
||||
.map(|code| unit(&doc, code)["proposals"][0]["id"].as_str().expect("open").to_owned())
|
||||
.collect();
|
||||
for p in &open {
|
||||
for c in &mut people {
|
||||
c.vote(p, "against").await;
|
||||
}
|
||||
}
|
||||
let sample = |state: AppState| async move {
|
||||
ld_server::participation::refresh(&state, true).await.expect("refresh");
|
||||
ld_server::participation::stored(&state, "essai").await.expect("stored").expect("measure").sample
|
||||
};
|
||||
assert_eq!(sample(state.clone()).await, 6);
|
||||
for (id, status) in [(&open[0], "applied"), (&open[1], "in_force")] {
|
||||
sqlx::query("UPDATE proposals SET status = ? WHERE id = ?")
|
||||
.bind(status)
|
||||
.bind(id)
|
||||
.execute(&state.db)
|
||||
.await
|
||||
.expect("status");
|
||||
}
|
||||
assert_eq!(sample(state.clone()).await, 6, "applied and in force stay counted");
|
||||
sqlx::query("UPDATE proposals SET status = 'superseded' WHERE id = ?")
|
||||
.bind(&open[2])
|
||||
.execute(&state.db)
|
||||
.await
|
||||
.expect("status");
|
||||
assert_eq!(sample(state.clone()).await, 5, "a status not named is out");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The base as production holds it on 2026-10-30, then the new version
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// The seed as it was then: no modules yet (ADR 0009 came after).
|
||||
fn seed_of_the_time(tag: &str) -> std::path::PathBuf {
|
||||
let today = Config::for_tests().seed_dir.join("acte-engagement-certification.json");
|
||||
let mut seed: Value = serde_json::from_str(&std::fs::read_to_string(today).expect("seed")).expect("json");
|
||||
seed.as_object_mut().expect("object").remove("modules");
|
||||
let dir = std::env::temp_dir().join(format!("ld-levels-seed-{tag}-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).expect("dir");
|
||||
std::fs::write(dir.join("acte-engagement-certification.json"), seed.to_string()).expect("write");
|
||||
dir
|
||||
}
|
||||
|
||||
/// Migrations up to 0004: the schema production runs before this version.
|
||||
async fn base_before_levels(tag: &str) -> (SqlitePool, AppState) {
|
||||
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
|
||||
@@ -325,6 +381,7 @@ async fn base_before_levels(tag: &str) -> (SqlitePool, AppState) {
|
||||
before.run(&pool).await.expect("migrations 1 to 4");
|
||||
let mut cfg = Config::for_tests();
|
||||
cfg.data_dir = std::env::temp_dir().join(format!("ld-levels-{tag}-{}", std::process::id()));
|
||||
cfg.seed_dir = seed_of_the_time(tag);
|
||||
let state = AppState::new(cfg, pool.clone()).await.expect("state");
|
||||
seed::documents(&state).await.expect("documents");
|
||||
// The words of the time: every article `standard`.
|
||||
|
||||
Reference in New Issue
Block a user