Lot S : tests serveur repris sans Propositions seed en vigueur (modules, overview), rustfmt
modules.rs : 0 article en vigueur et 35 propositions en cours dans une base neuve (I1, E1 et E2 sont des Propositions seed) ; l'ajout des modules à une base existante se rejoue sur le seed de production du 02/11 (textes en vigueur), comme inertia.rs. overview.rs : le vote porte sur la Proposition seed I1 ; 41 modules à voter, inchangé. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -198,7 +198,9 @@ fn check(seed: &SeedDoc) -> anyhow::Result<()> {
|
|||||||
anyhow::bail!("{}: a seed proposal is open, never in force", u.code);
|
anyhow::bail!("{}: a seed proposal is open, never in force", u.code);
|
||||||
}
|
}
|
||||||
match (u.puts_forward(), u.inertia.is_some()) {
|
match (u.puts_forward(), u.inertia.is_some()) {
|
||||||
(true, true) => anyhow::bail!("{}: a seed proposal starts at SEED_PROPOSAL_LEVEL, give it no level", u.code),
|
(true, true) => {
|
||||||
|
anyhow::bail!("{}: a seed proposal starts at SEED_PROPOSAL_LEVEL, give it no level", u.code)
|
||||||
|
}
|
||||||
(false, false) => anyhow::bail!("{}: no level of inertia", u.code),
|
(false, false) => anyhow::bail!("{}: no level of inertia", u.code),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -273,10 +275,7 @@ async fn instantiate(state: &AppState, seed: &SeedDoc, inst: &SeedInstance) -> a
|
|||||||
.await?;
|
.await?;
|
||||||
// The mark, written apart: only where the seed puts the version forward.
|
// The mark, written apart: only where the seed puts the version forward.
|
||||||
if v.seed_proposal && status == "open" {
|
if v.seed_proposal && status == "open" {
|
||||||
sqlx::query("UPDATE proposals SET seed_featured = 1 WHERE id = ?")
|
sqlx::query("UPDATE proposals SET seed_featured = 1 WHERE id = ?").bind(&pid).execute(&mut *tx).await?;
|
||||||
.bind(&pid)
|
|
||||||
.execute(&mut *tx)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
if status == "in_force" {
|
if status == "in_force" {
|
||||||
sqlx::query("UPDATE units SET in_force_id = ? WHERE id = ?")
|
sqlx::query("UPDATE units SET in_force_id = ? WHERE id = ?")
|
||||||
|
|||||||
@@ -247,7 +247,11 @@ fn target_of<'a>(seeds: &'a [SeedDoc], a: &Amendment) -> Option<(&'a SeedDoc, &'
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// What the database says of a version holding `status` (`None`: it does not).
|
/// What the database says of a version holding `status` (`None`: it does not).
|
||||||
pub(crate) async fn facts(conn: &mut SqliteConnection, proposal_id: &str, status: &str) -> anyhow::Result<Option<Facts>> {
|
pub(crate) async fn facts(
|
||||||
|
conn: &mut SqliteConnection,
|
||||||
|
proposal_id: &str,
|
||||||
|
status: &str,
|
||||||
|
) -> anyhow::Result<Option<Facts>> {
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
struct Row {
|
struct Row {
|
||||||
origin: String,
|
origin: String,
|
||||||
|
|||||||
@@ -409,7 +409,10 @@ async fn inertia_change_is_voted_at_the_level_of_the_article() {
|
|||||||
assert_eq!(again["id"], p.as_str());
|
assert_eq!(again["id"], p.as_str());
|
||||||
let (_, d) = a.call("GET", &format!("/api/proposals/{p}"), None).await;
|
let (_, d) = a.call("GET", &format!("/api/proposals/{p}"), None).await;
|
||||||
let low = u64::from(ld_server::seed::SEED_PROPOSAL_LEVEL);
|
let low = u64::from(ld_server::seed::SEED_PROPOSAL_LEVEL);
|
||||||
assert_eq!((d["proposal"]["inertiaTarget"].as_u64(), d["proposal"]["votedAtInertia"].as_u64()), (Some(5), Some(low)));
|
assert_eq!(
|
||||||
|
(d["proposal"]["inertiaTarget"].as_u64(), d["proposal"]["votedAtInertia"].as_u64()),
|
||||||
|
(Some(5), Some(low))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -183,9 +183,10 @@ async fn a_new_base_has_its_title_and_the_order_of_each_section() {
|
|||||||
let id = doc["doc"]["id"].as_str().expect("id");
|
let id = doc["doc"]["id"].as_str().expect("id");
|
||||||
let all = c.ok("GET", &format!("/api/docs/{id}/preview?mode=all"), None).await;
|
let all = c.ok("GET", &format!("/api/docs/{id}/preview?mode=all"), None).await;
|
||||||
assert_eq!(all["units"].as_array().expect("units").len(), 33);
|
assert_eq!(all["units"].as_array().expect("units").len(), 33);
|
||||||
// The list of the group: the articles in force; no module has an open proposal yet.
|
// The list of the group: the articles in force — none, I1, E1 and E2 are seed proposals
|
||||||
|
// (ADR 0011), three more open versions —; no module has an open proposal yet.
|
||||||
let g = c.ok("GET", "/api/groups/monnaie-libre", None).await;
|
let g = c.ok("GET", "/api/groups/monnaie-libre", None).await;
|
||||||
assert_eq!((g["docs"][0]["inForce"].as_u64(), g["docs"][0]["open"].as_u64()), (Some(3), Some(32)));
|
assert_eq!((g["docs"][0]["inForce"].as_u64(), g["docs"][0]["open"].as_u64()), (Some(0), Some(35)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -250,7 +251,8 @@ async fn articles_that_enter_or_leave_a_section_complete_an_order_under_vote() {
|
|||||||
.expect("id")
|
.expect("id")
|
||||||
.to_owned();
|
.to_owned();
|
||||||
yvv.vote(&p, "for").await;
|
yvv.vote(&p, "for").await;
|
||||||
// The list of the group counts it among the proposals in progress, never among the articles.
|
// The list of the group counts it among the proposals in progress, never among the articles
|
||||||
|
// (none in force: I1, E1 and E2 are seed proposals, ADR 0011).
|
||||||
let g = yvv.ok("GET", "/api/groups/essai", None).await;
|
let g = yvv.ok("GET", "/api/groups/essai", None).await;
|
||||||
let before = doc["units"]
|
let before = doc["units"]
|
||||||
.as_array()
|
.as_array()
|
||||||
@@ -259,7 +261,7 @@ async fn articles_that_enter_or_leave_a_section_complete_an_order_under_vote() {
|
|||||||
.map(|u| u["proposals"].as_array().expect("p").iter().filter(|x| x["status"] == "open").count() as u64);
|
.map(|u| u["proposals"].as_array().expect("p").iter().filter(|x| x["status"] == "open").count() as u64);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(g["docs"][0]["inForce"].as_u64(), g["docs"][0]["open"].as_u64()),
|
(g["docs"][0]["inForce"].as_u64(), g["docs"][0]["open"].as_u64()),
|
||||||
(Some(3), Some(before.sum::<u64>() + 1))
|
(Some(0), Some(before.sum::<u64>() + 1))
|
||||||
);
|
);
|
||||||
// A new article enters the section: at its end, and at the end of the order under vote.
|
// A new article enters the section: at its end, and at the end of the order under vote.
|
||||||
let created = chiara
|
let created = chiara
|
||||||
@@ -492,8 +494,17 @@ async fn a_new_article_goes_at_the_end_of_the_section_asked_for() {
|
|||||||
// A base born without modules, as production holds it, then the new version
|
// A base born without modules, as production holds it, then the new version
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// The seed production runs on 2026-11-02 (3f10a0e): I1, E1 and E2 in force, seeds a
|
||||||
|
/// minima. Since that day the seed puts them forward as seed proposals (ADR 0011,
|
||||||
|
/// tests/seed_proposals.rs); the addition of the modules is replayed on the seed of then.
|
||||||
|
fn production_config() -> Config {
|
||||||
|
let mut cfg = Config::for_tests();
|
||||||
|
cfg.seed_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/seed-production-2026-11-02");
|
||||||
|
cfg
|
||||||
|
}
|
||||||
|
|
||||||
fn seed_without_modules(tag: &str) -> std::path::PathBuf {
|
fn seed_without_modules(tag: &str) -> std::path::PathBuf {
|
||||||
let today = Config::for_tests().seed_dir.join("acte-engagement-certification.json");
|
let today = production_config().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");
|
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");
|
seed.as_object_mut().expect("object").remove("modules");
|
||||||
let dir = std::env::temp_dir().join(format!("ld-modules-seed-{tag}-{}", std::process::id()));
|
let dir = std::env::temp_dir().join(format!("ld-modules-seed-{tag}-{}", std::process::id()));
|
||||||
@@ -554,7 +565,7 @@ async fn an_existing_document_receives_its_modules_once_from_what_it_holds() {
|
|||||||
let reads = read_order(&c.doc("essai").await, "conclusion");
|
let reads = read_order(&c.doc("essai").await, "conclusion");
|
||||||
|
|
||||||
// The new version starts: each document receives its modules, from what it holds.
|
// The new version starts: each document receives its modules, from what it holds.
|
||||||
let state = AppState::new(Config::for_tests(), before.db.clone()).await.expect("state");
|
let state = AppState::new(production_config(), before.db.clone()).await.expect("state");
|
||||||
seed::ensure(&state).await.expect("seed");
|
seed::ensure(&state).await.expect("seed");
|
||||||
let rows: Vec<(String, String, Option<String>, Option<String>)> = sqlx::query_as(
|
let rows: Vec<(String, String, Option<String>, Option<String>)> = sqlx::query_as(
|
||||||
"SELECT d.group_slug, m.outcome, m.reason, m.created FROM module_amendments m JOIN documents d ON d.id = m.doc_id
|
"SELECT d.group_slug, m.outcome, m.reason, m.created FROM module_amendments m JOIN documents d ON d.id = m.doc_id
|
||||||
@@ -619,7 +630,7 @@ async fn an_existing_document_receives_its_modules_once_from_what_it_holds() {
|
|||||||
assert_eq!(json["modules"]["orders"][1]["articles"], json!(["T1", "E1", "E2"]));
|
assert_eq!(json["modules"]["orders"][1]["articles"], json!(["T1", "E1", "E2"]));
|
||||||
|
|
||||||
// Once only: the next start adds nothing.
|
// Once only: the next start adds nothing.
|
||||||
let again = AppState::new(Config::for_tests(), state.db.clone()).await.expect("state");
|
let again = AppState::new(production_config(), state.db.clone()).await.expect("state");
|
||||||
seed::ensure(&again).await.expect("seed");
|
seed::ensure(&again).await.expect("seed");
|
||||||
let n: i64 =
|
let n: i64 =
|
||||||
sqlx::query_scalar("SELECT COUNT(*) FROM units WHERE kind <> 'article'").fetch_one(&again.db).await.expect("n");
|
sqlx::query_scalar("SELECT COUNT(*) FROM units WHERE kind <> 'article'").fetch_one(&again.db).await.expect("n");
|
||||||
|
|||||||
@@ -147,15 +147,16 @@ async fn decisions_one_line_per_doc_with_the_modules_left_to_vote() {
|
|||||||
assert_eq!(docs.len(), 1);
|
assert_eq!(docs.len(), 1);
|
||||||
assert_eq!(docs[0]["slug"], "acte-engagement-certification");
|
assert_eq!(docs[0]["slug"], "acte-engagement-certification");
|
||||||
assert_eq!(docs[0]["title"]["fr"], "Acte d'engagement Certification");
|
assert_eq!(docs[0]["title"]["fr"], "Acte d'engagement Certification");
|
||||||
// 3 seeds a minima + 30 open articles, the two variants counting once; the title of the
|
// 3 seed proposals (ADR 0011) + 30 open articles, the two variants counting once; the title
|
||||||
// document and the order of 7 sections (« ordonnancement » holds one article: nothing to decide).
|
// of the document and the order of 7 sections (« ordonnancement » holds one article: nothing
|
||||||
|
// to decide).
|
||||||
assert_eq!(docs[0]["toVote"], 41);
|
assert_eq!(docs[0]["toVote"], 41);
|
||||||
assert_eq!(docs[0]["withoutMyVote"], 41);
|
assert_eq!(docs[0]["withoutMyVote"], 41);
|
||||||
|
|
||||||
let doc = the_doc(&mut c, "essai").await;
|
let doc = the_doc(&mut c, "essai").await;
|
||||||
c.vote(&version(&doc, "E3", "0"), "for").await;
|
c.vote(&version(&doc, "E3", "0"), "for").await;
|
||||||
c.vote(&version(&doc, "E3", "1"), "against").await;
|
c.vote(&version(&doc, "E3", "1"), "against").await;
|
||||||
c.vote(&version(&doc, "I1", "inForce"), "for").await;
|
c.vote(&version(&doc, "I1", "0"), "for").await;
|
||||||
let d = decisions(&mut c, "essai").await;
|
let d = decisions(&mut c, "essai").await;
|
||||||
assert_eq!((d["docs"][0]["toVote"].as_u64(), d["docs"][0]["withoutMyVote"].as_u64()), (Some(41), Some(39)));
|
assert_eq!((d["docs"][0]["toVote"].as_u64(), d["docs"][0]["withoutMyVote"].as_u64()), (Some(41), Some(39)));
|
||||||
// Someone else has not voted yet.
|
// Someone else has not voted yet.
|
||||||
|
|||||||
@@ -140,7 +140,9 @@ async fn a_new_base_is_born_with_three_seed_proposals_and_no_article_in_force()
|
|||||||
assert_eq!(marked.len(), 3, "{group}");
|
assert_eq!(marked.len(), 3, "{group}");
|
||||||
let title = &doc["modules"]["title"]["inForce"];
|
let title = &doc["modules"]["title"]["inForce"];
|
||||||
assert_eq!((title["seedMinimal"].as_bool(), title["seedProposal"].as_bool()), (Some(true), Some(false)));
|
assert_eq!((title["seedMinimal"].as_bool(), title["seedProposal"].as_bool()), (Some(true), Some(false)));
|
||||||
assert!(doc["modules"]["orders"].as_array().expect("orders").iter().all(|o| o["inForce"]["seedMinimal"] == true));
|
assert!(
|
||||||
|
doc["modules"]["orders"].as_array().expect("orders").iter().all(|o| o["inForce"]["seedMinimal"] == true)
|
||||||
|
);
|
||||||
// The counts: no article in force, three more open versions; still 41 modules to vote.
|
// The counts: no article in force, three more open versions; still 41 modules to vote.
|
||||||
let g = c.get(&format!("/api/groups/{group}")).await;
|
let g = c.get(&format!("/api/groups/{group}")).await;
|
||||||
assert_eq!((g["docs"][0]["inForce"].as_u64(), g["docs"][0]["open"].as_u64()), (Some(0), Some(35)), "{group}");
|
assert_eq!((g["docs"][0]["inForce"].as_u64(), g["docs"][0]["open"].as_u64()), (Some(0), Some(35)), "{group}");
|
||||||
@@ -203,7 +205,8 @@ const PREAMBULE: [(&str, &str, &str); 3] = [
|
|||||||
/// The seed production runs on 2026-11-02 (3f10a0e): I1, E1 and E2 in force, seeds a
|
/// The seed production runs on 2026-11-02 (3f10a0e): I1, E1 and E2 in force, seeds a
|
||||||
/// minima, I1 and E1 at level 3 and 4.
|
/// minima, I1 and E1 at level 3 and 4.
|
||||||
fn production_seed() -> Value {
|
fn production_seed() -> Value {
|
||||||
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/seed-production-2026-11-02/acte-engagement-certification.json");
|
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
.join("tests/fixtures/seed-production-2026-11-02/acte-engagement-certification.json");
|
||||||
serde_json::from_str(&std::fs::read_to_string(path).expect("seed")).expect("json")
|
serde_json::from_str(&std::fs::read_to_string(path).expect("seed")).expect("json")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,7 +429,12 @@ async fn the_production_base_turns_its_untouched_texts_into_seed_proposals_once(
|
|||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
changes,
|
changes,
|
||||||
[line("essai", "E1", "4"), line("essai", "E2", "4"), line("essai", "I1", "3"), line("monnaie-libre", "E2", "4")]
|
[
|
||||||
|
line("essai", "E1", "4"),
|
||||||
|
line("essai", "E2", "4"),
|
||||||
|
line("essai", "I1", "3"),
|
||||||
|
line("monnaie-libre", "E2", "4")
|
||||||
|
]
|
||||||
);
|
);
|
||||||
let mut c = Client::of(&state);
|
let mut c = Client::of(&state);
|
||||||
c.login("Chiara", false).await;
|
c.login("Chiara", false).await;
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ impl Client {
|
|||||||
async fn new() -> (Self, AppState) {
|
async fn new() -> (Self, AppState) {
|
||||||
let db = open_db("sqlite::memory:").await.expect("db");
|
let db = open_db("sqlite::memory:").await.expect("db");
|
||||||
let mut cfg = Config::for_tests();
|
let mut cfg = Config::for_tests();
|
||||||
cfg.seed_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/seed-production-2026-11-02");
|
cfg.seed_dir =
|
||||||
|
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/seed-production-2026-11-02");
|
||||||
let state = AppState::new(cfg, db).await.expect("state");
|
let state = AppState::new(cfg, db).await.expect("state");
|
||||||
seed::ensure(&state).await.expect("seed");
|
seed::ensure(&state).await.expect("seed");
|
||||||
(Self { router: app(state.clone()), cookie: None }, state)
|
(Self { router: app(state.clone()), cookie: None }, state)
|
||||||
|
|||||||
Reference in New Issue
Block a user