Files
core/test/rust/compiler/upload.rs
T
2026-11-03 03:32:07 +00:00

117 lines
4.6 KiB
Rust

use super::package::packages;
const STORAGE: &str = "public type PutResult { Stored(key: Uuid)\nerror Failed(message: Text) }\npublic put(upload: Upload) (key: Uuid) { key = stored(dever.system.uploadPut(upload)) }\nstored(result: PutResult.Stored(value)) (key: Uuid) { key = value }\nstored(result: PutResult.Failed(message)) (key: Uuid) { fail(PutResult.Failed(message)) }";
const APP: &str = "accept(file: Upload, caption: Text) (size: Int, name: Text) { size = dever.api.uploadSize(file)\nname = caption + dever.api.uploadFilename(file)\ndever.api.closeUpload(file) }";
#[test]
fn upload_metadata_then_close_preserves_a_single_owned_post_input() {
let program = packages(&[
("demo.media.app", APP),
("demo.media.api", "public post accept = app.accept"),
])
.unwrap();
assert!(
dever::compiler::build::runtime_features(&program, &program.entries().collect())
.contains(&"upload")
);
for api in ["public get accept = app.accept", "cmd accept = app.accept"] {
assert!(
packages(&[("demo.media.app", APP), ("demo.media.api", api)]).is_err(),
"{api}"
);
}
}
#[test]
fn upload_rejects_duplication_nesting_output_and_helpers() {
for app in [
"accept(file: Upload) () {}",
"accept(file: Upload) () { dever.api.closeUpload(file)\ndever.api.closeUpload(file) }",
"accept(file: Upload) (size: Int) { dever.api.closeUpload(file)\nsize = dever.api.uploadSize(file) }",
"accept(file: Upload) () { copy = file\ndever.api.closeUpload(copy) }",
"accept(file: Upload) (out: Upload) { out = file }",
"type Container { file: Upload }",
"type Container { Wrapped(file: Upload) }",
"accept(files: List<Upload>) () {}",
"accept(file: Upload?) () {}",
"accept(file: Upload, other: Upload) () { dever.api.closeUpload(file)\ndever.api.closeUpload(other) }",
"accept(file: Upload) () { helper(file) }\nhelper(file: Upload) () { dever.api.closeUpload(file) }",
] {
let error = packages(&[("demo.media.app", app)]).unwrap_err();
assert!(
!error.code.starts_with('P'),
"syntax did not reach ownership check: {error:?}"
);
}
assert!(packages(&[("demo.media.domain", "ignore(file: Upload) () {}")]).is_err());
}
#[test]
fn upload_storage_requires_a_same_domain_port_and_never_escapes_to_tasks() {
let app = "accept(file: Upload) (key: Uuid) { key = port.store(file) }";
let port = "store(file: Upload) (key: Uuid) fails dever.storage.PutResult";
let adapter = "port.store(file: Upload) (key: Uuid) { key = dever.storage.put(file) }";
let checked = |app: &str| {
packages(&[
("dever.storage", STORAGE),
("demo.media.app", app),
("demo.media.port", port),
("demo.media.adapter.disk", adapter),
("demo.media.api", "public post accept = app.accept"),
])
};
checked(app).unwrap();
for app in [
"accept(file: Upload) (key: Uuid) { key = dever.storage.put(file) }",
"accept(file: Upload) (key: Uuid) { task = run(port.store(file))\nkey = wait(task) }",
"accept(file: Upload) (key: Uuid) { key = blocking(port.store(file)) }",
"accept(file: Upload) (key: Uuid) { key = parallel(port.store(file)) }",
] {
assert!(checked(app).is_err(), "{app}");
}
}
#[test]
fn upload_storage_remains_an_external_effect_for_database_transactions() {
let check = |prefix: &str, api: &str| {
packages(&[
("dever.storage", STORAGE),
("dever.database", super::database::ERROR),
("demo.media.model", "type Entry { name: Text }"),
(
"demo.media.app",
&format!(
"{prefix}accept(file: Upload) (key: Uuid) {{ row = model.create({{ name = \"upload\" }})\nkey = port.store(file) }}"
),
),
(
"demo.media.port",
"store(file: Upload) (key: Uuid) fails dever.storage.PutResult",
),
(
"demo.media.adapter",
"port.store(file: Upload) (key: Uuid) { key = dever.storage.put(file) }",
),
("demo.media.api", api),
])
};
assert_eq!(
check("", "public post accept = app.accept")
.unwrap_err()
.code,
"C014"
);
assert_eq!(
check("transaction ", "public post accept = app.accept")
.unwrap_err()
.code,
"C014"
);
assert_eq!(
check("", "public long post accept = app.accept")
.unwrap_err()
.code,
"C014"
);
}