use std::collections::BTreeMap;
use lex_store::api::{classify_api_change, public_api_at_op, ApiChange};
use lex_store::{Store, DEFAULT_BRANCH};
use lex_syntax::parse_source;
fn fns(src: &str) -> BTreeMap<String, lex_ast::FnDecl> {
lex_ast::canonicalize_program(&parse_source(src).expect("parse"))
.into_iter()
.filter_map(|st| match st {
lex_ast::Stage::FnDecl(fd) => Some((fd.name.clone(), fd)),
_ => None,
})
.collect()
}
fn publish(
s: &Store,
old: &BTreeMap<String, lex_ast::FnDecl>,
src: &str,
) -> (String, BTreeMap<String, lex_ast::FnDecl>) {
let stages = lex_ast::canonicalize_program(&parse_source(src).expect("parse"));
let new = fns(src);
let et: BTreeMap<String, lex_ast::TypeDecl> = Default::default();
let diff = lex_vcs::compute_diff_with_types(old, &new, &et, &et, true);
let imports = lex_vcs::ImportMap::new();
let head = s
.publish_program(DEFAULT_BRANCH, &stages, &diff, &imports, true)
.unwrap()
.head_op
.expect("head_op");
(head, new)
}
#[test]
fn api_diff_classifies_breaking_additive_and_none() {
let tmp = tempfile::tempdir().unwrap();
let s = Store::open(tmp.path()).unwrap();
let (h1, f1) = publish(&s, &Default::default(), "fn f(x :: Int) -> Int { x }\n");
let (h2, f2) = publish(&s, &f1, "fn f(x :: Int) -> Int { x }\nfn g() -> Int { 7 }\n");
let (h3, _f3) = publish(&s, &f2, "fn f(x :: Int) -> Int { x + 0 }\nfn g() -> Int { 7 }\n");
let a1 = public_api_at_op(&s, &h1).unwrap();
let a2 = public_api_at_op(&s, &h2).unwrap();
let a3 = public_api_at_op(&s, &h3).unwrap();
assert_eq!(a1.len(), 1, "v1 api: {a1:?}");
assert_eq!(a2.len(), 2, "v2 api: {a2:?}");
assert!(matches!(classify_api_change(&a1, &a2), ApiChange::Additive(_)), "v1→v2 additive");
assert_eq!(classify_api_change(&a2, &a3), ApiChange::None, "v2→v3 body-only = none");
assert!(matches!(classify_api_change(&a2, &a1), ApiChange::Breaking(_)), "removal = breaking");
}