feat: publish Dever core with project scaffolding and versioned AI skills

This commit is contained in:
Antigravity committed 2026-10-04 13:37:30 +00:00
1 parent 70fa73cdc2
commit ac458ffa7f
960 files changed
+221319 -1163

No files matched your search

+489
View File
@@ -0,0 +1,489 @@
mod support;
use std::fs;
use std::process::Command;
use dever_core::wire::Policy;
use dever_runtime::{time, wire};
#[test]
fn time_calendar_precision_and_checked_arithmetic() {
for (text, expected) in [
("1970-01-01T00:00:00Z", 0),
("1969-12-31T23:59:59.999Z", -1),
("1970-01-01T08:00:00+08:00", 0),
("2000-02-29T00:00:00Z", 951_782_400_000),
] {
let value = time::parse_datetime(text).unwrap();
assert_eq!(value, expected);
assert_eq!(
time::parse_datetime(&time::format_datetime(value).unwrap()).unwrap(),
value
);
}
assert_eq!(time::format_datetime(0).unwrap(), "1970-01-01T00:00:00Z");
assert_eq!(
time::format_datetime(-1).unwrap(),
"1969-12-31T23:59:59.999Z"
);
for text in [
"1900-02-29T00:00:00Z",
"2000-02-29",
"2016-12-31T23:59:60Z",
"1970-01-01T00:00:00.0001Z",
"1970-01-01T00:00:00.0000000001Z",
] {
assert!(time::parse_datetime(text).is_err(), "{text}");
}
for text in ["0000-01-01", "1969-12-31", "2000-02-29", "9999-12-31"] {
assert_eq!(
time::format_date(time::parse_date(text).unwrap()).unwrap(),
text
);
}
for text in ["1900-02-29", "2024-2-01", "2024-02-30", "+2024-02-01"] {
assert!(time::parse_date(text).is_err());
}
for text in ["00:00:00", "23:59:59.999", "12:30:05.001"] {
assert_eq!(
time::format_time(time::parse_time(text).unwrap()).unwrap(),
text
);
}
for text in ["24:00:00", "23:59:60", "12:00:00.1", "12:00:00.0001"] {
assert!(time::parse_time(text).is_err());
}
assert!(time::format_time(86_400_000).is_err());
assert!(time::format_time(-1).is_err());
assert!(time::format_date(1).is_err());
assert!(time::format_datetime(i64::MAX).is_err());
assert!(time::format_datetime(i64::MIN).is_err());
assert!(time::add(i64::MAX, 1).is_err());
assert!(time::subtract(i64::MIN, 1).is_err());
assert!(time::difference(i64::MAX, -1).is_err());
assert_eq!(time::add(-1, time::duration(1)).unwrap(), 0);
assert_eq!(time::difference(-1, 0).unwrap(), -1);
}
#[test]
fn parser_rejects_ambiguous_and_unbounded_input_without_losing_json_numbers() {
for text in [
r#"{"a":{"x":1,"x":2}}"#,
r#"[{"x":1,"x":2}]"#,
"true false",
"NaN",
"Infinity",
"[1,]",
r#""\ud800""#,
] {
assert!(wire::parse(text).is_err(), "{text}");
}
let precise =
r#"{"big":1234567890123456789012345678901234567890,"tiny":1.234567890123456789e-900}"#;
assert_eq!(wire::parse(precise).unwrap().raw(), precise);
assert_eq!(
wire::parse("-9223372036854775808").unwrap().int().unwrap(),
i64::MIN
);
for text in ["9223372036854775808", "1.0", "1e0", "true", "\"1\""] {
assert!(wire::parse(text).unwrap().int().is_err());
}
assert!(wire::parse("1e900").unwrap().float().is_err());
let at_depth = format!(
"{}0{}",
"[".repeat(wire::MAX_DEPTH),
"]".repeat(wire::MAX_DEPTH)
);
assert!(wire::parse(&at_depth).is_ok());
assert!(wire::parse(&format!("[{at_depth}]")).is_err());
assert!(wire::parse(&format!("\"{}\"", "x".repeat(wire::MAX_BYTES))).is_err());
assert!(wire::parse(&format!("[{}]", vec!["0"; wire::MAX_ELEMENTS].join(","))).is_err());
}
#[test]
fn encoder_enforces_limits_and_preserves_raw_json_in_api_envelope() {
let raw = "1234567890123456789012345678901234567890";
let mut writer = wire::Encoder::default();
writer.begin_object().unwrap();
writer.key("raw").unwrap();
writer.json(raw).unwrap();
writer.key("when").unwrap();
writer.text(&time::format_datetime(-1).unwrap()).unwrap();
writer.end().unwrap();
let response = dever_runtime::api::success(writer.finish().unwrap());
let text = std::str::from_utf8(response.body.values()).unwrap();
assert_eq!(response.status, 200);
assert_eq!(
text,
format!(
"{{\"code\":0,\"message\":\"ok\",\"data\":{{\"raw\":{raw},\"when\":\"1969-12-31T23:59:59.999Z\"}}}}"
)
);
for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
assert!(wire::Encoder::default().float(value).is_err());
}
let mut writer = wire::Encoder::default();
writer.begin_array().unwrap();
for _ in 1..wire::MAX_ELEMENTS {
writer.null().unwrap();
}
assert!(writer.null().is_err());
assert!(wire::Encoder::default().json(r#"{"x":1,"x":2}"#).is_err());
let mut writer = wire::Encoder::default();
writer.begin_object().unwrap();
writer.key("missing").unwrap();
assert!(writer.end().is_err());
assert!(writer.finish().is_err());
let mut writer = wire::Encoder::default();
writer.begin_array().unwrap();
writer.text(&"x".repeat(wire::MAX_BYTES - 3)).unwrap();
assert!(writer.end().is_err());
assert!(writer.finish().is_err());
let mut writer = wire::Encoder::default();
writer.begin_array().unwrap();
writer.text(&"x".repeat(wire::MAX_BYTES - 4)).unwrap();
assert!(writer.int(1).is_err());
assert!(writer.end().is_err());
assert!(writer.finish().is_err());
}
const TYPES: &str = r#"
public type Entry { label: Text
count: Int? }
public type Payload {
active: Bool
count: Int
ratio: Float
amount: Decimal
label: Text
key: Id
uuid: Uuid
instant: DateTime
date: Date
clock: Time
elapsed: Duration
raw: Json
entries: List<Entry>
}
public type Credentials { password: Secret }
public main() () {}
"#;
#[test]
fn schema_policy_and_field_contracts_have_one_owner() {
let program = support::checked(&support::sources(TYPES));
let output = program.wire_schema("main.Payload", Policy::Output).unwrap();
let input = program
.wire_schema("main.Payload", Policy::ApiInput)
.unwrap();
let job = program.wire_schema("main.Payload", Policy::Job).unwrap();
assert_ne!(output.fingerprint(), input.fingerprint());
assert_ne!(output.fingerprint(), job.fingerprint());
let shifted = support::checked(&support::sources(&format!(
"public type Unrelated {{ value: Text }}\n{TYPES}"
)));
assert_eq!(
output.fingerprint(),
shifted
.wire_schema("main.Payload", Policy::Output)
.unwrap()
.fingerprint()
);
for policy in [Policy::ApiInput, Policy::SettingInput] {
let schema = program.wire_schema("main.Credentials", policy).unwrap();
let code = dever_core::native::wire::emit(&schema, 0);
assert!(code.contains("Secret::from_input"));
assert!(!code.contains("_encode"));
}
for policy in [Policy::Output, Policy::Job] {
assert!(program.wire_schema("main.Credentials", policy).is_err());
}
for declaration in [
"public type Bad { private name: Text }",
"public type Bad { port: Int >= 1 and <= 65535 }",
"public type Bad { stream: Stream<Int> }",
"public type Bad { values: Map<Text, Int> }",
"public type Bad { One\nTwo }",
] {
let program = support::checked(&support::sources(&format!(
"{declaration}\npublic main() () {{}}"
)));
for policy in [
Policy::Output,
Policy::ApiInput,
Policy::SettingInput,
Policy::Job,
] {
assert!(
program.wire_schema("main.Bad", policy).is_err(),
"{declaration}"
);
}
}
let mut bounded = support::sources("main() () {}");
bounded.add(
"counter/api/value.dever",
"get_value() (response: Int >= 0) { response = 1 }",
);
assert!(
dever_core::check(&bounded)
.unwrap_err()
.iter()
.any(|error| error
.message
.contains("wire fields must be public and unconstrained"))
);
}
#[test]
fn wire_schemas_do_not_change_model_time_storage_identity() {
let mut sources = support::sources("main() () { calendar.event.save() }");
sources.add(
"calendar/event/model.dever",
"type Event { instant: DateTime\ndate: Date\ntime: Time\nelapsed: Duration }",
);
sources.add(
"calendar/event/app.dever",
r#"type ClockView { instant: DateTime
date: Date
time: Time
elapsed: Duration }
save() () {
stored = model.create({
instant = dever.time.parse_datetime("1969-12-31T23:59:59.999Z")
date = dever.time.parse_date("2000-02-29")
time = dever.time.parse_time("23:59:59.999")
elapsed = dever.time.duration(-7)
})
}"#,
);
let program = support::checked(&sources);
let before = program.model_snapshot();
for (name, ty) in [
("instant", "DateTime"),
("date", "Date"),
("time", "Time"),
("elapsed", "Duration"),
] {
assert!(
before.contains(&format!("field {name} {ty} nullable=false")),
"{before}"
);
}
for policy in [
Policy::Output,
Policy::ApiInput,
Policy::SettingInput,
Policy::Job,
] {
program
.wire_schema("calendar.event.ClockView", policy)
.unwrap();
let id = program
.wire_schema("calendar.event.model.id", policy)
.unwrap();
assert!(
id.fingerprint()
.contains("ModelId(\"calendar.event.model.id\")")
);
let model = program
.wire_schema("calendar.event.model.Event", policy)
.unwrap();
assert!(
model
.fingerprint()
.contains("ModelId(\"calendar.event.model.id\")")
);
}
assert_eq!(before, program.model_snapshot());
let generated = dever_core::native::emit(&program, &sources, "main.main").unwrap();
assert!(generated.contains("f0: i64,\nf1: i64,\nf2: i64,\nf3: i64,"));
assert_eq!(
generated
.matches("dever_runtime::orm::Value::Int(p")
.count(),
4
);
}
#[test]
fn official_time_bridge_is_pure_and_matches_reference() {
let source = r#"public main() (stamp: Text, date: Text, clock: Text, delta: Duration, invalid: dever.time.DateResult) pure {
instant = dever.time.parse_datetime("1969-12-31T23:59:59.999Z")
stamp = dever.time.format_datetime(dever.time.add(instant, dever.time.duration(1)))
date = dever.time.format_date(dever.time.parse_date("2000-02-29"))
clock = dever.time.format_time(dever.time.parse_time("23:59:59.999"))
delta = dever.time.difference(dever.time.subtract(instant, dever.time.duration(1)), instant)
invalid = result(dever.time.parse_date("1900-02-29"))
}"#;
let sources = support::sources(source);
let program = support::checked(&sources);
let expected = "stamp = 1970-01-01T00:00:00Z\ndate = 2000-02-29\nclock = 23:59:59.999\ndelta = -1\ninvalid = dever.time.DateResult.Failed(invalid calendar Date)\n";
assert_eq!(support::stdout(source), expected);
let reference = dever_core::reference::evaluate(&program, "main.main").unwrap();
assert_eq!(reference.stdout, expected);
support::rejected(
"public main() (value: DateTime) pure { value = dever.time.now() }",
"pure",
);
}
#[test]
fn time_api_has_equal_plain_and_markdown_contracts() {
let code = "main() (value: Text) pure { value = dever.time.format_datetime(dever.time.parse_datetime(\"2000-02-29T08:00:00+08:00\")) }";
let plain = support::sources(code);
let mut markdown = dever_core::source::SourceMap::default();
markdown.add("main.dever.md", format!("# 时间转换\n\n验证 UTC 毫秒合同。\n\n- 包:`main`\n- 公开类型:无\n- 公开方法:无\n- 使用:无\n\n## 入口\n\n转换日期时间。\n\n- 函数:`main`\n- 输入:无\n- 输出:\n - `value: Text`:UTC 时间。\n\n```dever\n{code}\n```\n"));
let plain_program = support::checked(&plain);
let markdown_program = support::checked(&markdown);
assert_eq!(
plain_program.api_snapshot(),
markdown_program.api_snapshot()
);
let expected = "value = 2000-02-29T00:00:00Z\n";
for program in [&plain_program, &markdown_program] {
let output = dever_core::reference::evaluate(program, "main.main").unwrap();
assert!(output.fault.is_none());
assert_eq!(output.stdout, expected);
}
}
#[test]
fn generated_api_uses_shared_time_codec_without_a_listener() {
let mut sources = support::sources("main() () { dever.api.serve() }");
sources.add("calendar/api/time.dever", "get_now() (response: DateTime) { response = dever.time.parse_datetime(\"1970-01-01T08:00:00+08:00\") }\nget_again() (response: DateTime) { response = dever.time.parse_datetime(\"1969-12-31T23:59:59.999Z\") }");
let program = support::checked(&sources);
let mut code = dever_core::native::emit(&program, &sources, "main.main")
.unwrap()
.replace("fn main()", "fn unused_main()");
assert_eq!(code.matches("fn wire_0_encode").count(), 1);
assert!(!code.contains("fn wire_1_encode"));
code.push_str(r#"
fn main() {
let request = dever_runtime::http::Request {
method: "GET".into(), target: "/calendar/time/now".into(), headers: vec![], body: Bytes::from_text(""),
};
let response = task::run_entry(dever_runtime::api::scoped_request(request, api_route)).unwrap();
assert_eq!(response.status, 200);
assert_eq!(std::str::from_utf8(response.body.values()).unwrap(), "{\"code\":0,\"message\":\"ok\",\"data\":\"1970-01-01T00:00:00Z\"}");
}
"#);
compile_and_run(&code);
}
#[test]
fn generated_concrete_codec_roundtrips_and_rejects_bad_records() {
let sources = support::sources(TYPES);
let program = support::checked(&sources);
let mut code = dever_core::native::emit(&program, &sources, "main.main")
.unwrap()
.replace("fn main()", "fn unused_main()");
code.push_str(&dever_core::native::wire::emit(
&program.wire_schema("main.Payload", Policy::Output).unwrap(),
0,
));
code.push_str(&dever_core::native::wire::emit(
&program
.wire_schema("main.Credentials", Policy::SettingInput)
.unwrap(),
1,
));
code.push_str(r###"
fn main() {
let raw = r#"{"active":true,"count":-9223372036854775808,"ratio":-0.0,"amount":"1234567890123456789012345678901234","label":"你好","key":"9007199254740993","uuid":"12345678-1234-5678-9012-123456789abc","instant":"1969-12-31T23:59:59.999Z","date":"2000-02-29","clock":"23:59:59.999","elapsed":-7,"raw":1234567890123456789012345678901234567890,"entries":[{"label":"entry"}]}"#;
let decoded = wire_0_decode(raw).unwrap();
assert_eq!(decoded.f1, i64::MIN);
assert_eq!(decoded.f2.to_bits(), (-0.0f64).to_bits());
assert_eq!(decoded.f3, DecimalValue::parse("1234567890123456789012345678901234").unwrap());
assert_eq!(decoded.f4, "你好");
assert_eq!(decoded.f5.0, "9007199254740993");
assert_eq!(decoded.f6.to_string(), "12345678-1234-5678-9012-123456789abc");
assert_eq!(decoded.f7, -1);
assert_eq!(decoded.f8, 951782400000);
assert_eq!(decoded.f9, 86399999);
assert_eq!(decoded.f10, -7);
assert!(decoded.f12.values()[0].f1.is_none());
let encoded = wire_0_encode(&decoded).unwrap();
assert!(encoded.as_str().contains("1234567890123456789012345678901234567890"));
assert!(encoded.as_str().contains("1969-12-31T23:59:59.999Z"));
let again = wire_0_decode(encoded.as_str()).unwrap();
assert_eq!(encoded.as_str(), wire_0_encode(&again).unwrap().as_str());
assert!(wire_0_decode(&raw.replace("\"count\":-9223372036854775808,", "")).is_err());
assert!(wire_0_decode(&raw.replace("\"active\":true", "\"extra\":true,\"active\":true")).is_err());
assert!(wire_0_decode(&raw.replace("\"label\":\"entry\"", "\"label\":\"entry\",\"label\":\"again\"")).is_err());
assert!(wire_0_decode(&raw.replace("\"ratio\":-0.0", "\"ratio\":1e900")).is_err());
assert!(wire_1_decode(r#"{"password":"secret"}"#).is_ok());
assert!(wire_1_decode(r#"{"password":42}"#).is_err());
}
"###);
compile_and_run(&code);
}
fn compile_and_run(code: &str) {
let workspace = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let built = Command::new("cargo")
.current_dir(&workspace)
.args([
"build",
"--locked",
"--offline",
"-p",
"dever-runtime",
"--features",
"api",
"--config",
"profile.dev.debug=0",
"--message-format=json",
])
.output()
.unwrap();
assert!(
built.status.success(),
"{}",
String::from_utf8_lossy(&built.stderr)
);
let artifact = String::from_utf8(built.stdout)
.unwrap()
.lines()
.filter_map(|line| serde_json::from_str::<serde_json::Value>(line).ok())
.find(|value| {
value["reason"] == "compiler-artifact" && value["target"]["name"] == "dever_runtime"
})
.unwrap();
let library = artifact["filenames"]
.as_array()
.unwrap()
.iter()
.filter_map(|value| value.as_str())
.find(|name| name.ends_with(".rlib"))
.unwrap();
let directory = support::temp::TemporaryDirectory::new();
let source = directory.path().join("codec.rs");
let executable = directory.path().join("codec");
fs::write(&source, code).unwrap();
let dependencies = std::path::Path::new(library).parent().unwrap().join("deps");
let built = Command::new("rustc")
.args(["--edition=2024", "-C", "debuginfo=0", "--extern"])
.arg(format!("dever_runtime={library}"))
.arg("-L")
.arg(format!("dependency={}", dependencies.display()))
.arg(&source)
.arg("-o")
.arg(&executable)
.output()
.unwrap();
assert!(
built.status.success(),
"{}",
String::from_utf8_lossy(&built.stderr)
);
let result = Command::new(executable).output().unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
}