Unit test sea-orm-codegen

This commit is contained in:
Billy Chan 2021-07-03 00:02:45 +08:00 committed by Chris Tsang
parent 1e6a778a0f
commit a94224e0f2
17 changed files with 1297 additions and 32 deletions

View File

@ -31,3 +31,4 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: test
args: --all

View File

@ -38,6 +38,7 @@ futures-util = { version = "^0.3" }
rust_decimal = { version = "^1", optional = true }
sea-query = { version = "^0.12" }
sea-orm-macros = { path = "sea-orm-macros", optional = true }
sea-orm-codegen = { path = "sea-orm-codegen", optional = true }
serde = { version = "^1.0", features = [ "derive" ] }
sqlx = { version = "^0.5", optional = true }
strum = { git = "https://github.com/SeaQL/strum.git", branch = "sea-orm", version = "^0.21", features = [ "derive", "sea-orm" ] }
@ -51,8 +52,9 @@ sea-orm = { path = ".", features = ["sqlx-sqlite", "sqlx-json", "sqlx-chrono", "
[features]
debug-print = []
default = [ "macros", "with-json", "with-chrono", "with-rust_decimal", "mock" ]
default = [ "macros", "codegen", "with-json", "with-chrono", "with-rust_decimal", "mock" ]
macros = [ "sea-orm-macros" ]
codegen = [ "sea-orm-codegen" ]
mock = []
with-json = [ "serde_json", "sea-query/with-json" ]
with-chrono = [ "chrono", "sea-query/with-chrono" ]
@ -69,4 +71,4 @@ runtime-async-std-native-tls = [ "sqlx/runtime-async-std-native-tls" ]
runtime-tokio-native-tls = [ "sqlx/runtime-tokio-native-tls" ]
runtime-actix-rustls = [ "sqlx/runtime-actix-rustls" ]
runtime-async-std-rustls = [ "sqlx/runtime-async-std-rustls" ]
runtime-tokio-rustls = [ "sqlx/runtime-tokio-rustls" ]
runtime-tokio-rustls = [ "sqlx/runtime-tokio-rustls" ]

View File

@ -23,3 +23,7 @@ syn = { version = "^1", default-features = false, features = [ "derive", "parsin
quote = "^1"
heck = "^0.3"
proc-macro2 = "^1"
[dev-dependencies]
async-std = { version = "^1.9", features = [ "attributes" ] }
sea-orm = { path = "../", features = ["mock", "sqlx-json", "sqlx-chrono", "runtime-async-std-native-tls"] }

View File

@ -22,7 +22,7 @@ impl Column {
}
pub fn get_rs_type(&self) -> TokenStream {
let ident = match self.col_type {
let ident: TokenStream = match self.col_type {
ColumnType::Char(_)
| ColumnType::String(_)
| ColumnType::Text
@ -32,18 +32,18 @@ impl Column {
| ColumnType::Date
| ColumnType::Json
| ColumnType::JsonBinary
| ColumnType::Custom(_) => format_ident!("String"),
ColumnType::TinyInteger(_) => format_ident!("i8"),
ColumnType::SmallInteger(_) => format_ident!("i16"),
ColumnType::Integer(_) => format_ident!("i32"),
ColumnType::BigInteger(_) => format_ident!("i64"),
ColumnType::Float(_) | ColumnType::Decimal(_) | ColumnType::Money(_) => {
format_ident!("f32")
}
ColumnType::Double(_) => format_ident!("f64"),
ColumnType::Binary(_) => format_ident!("Vec<u8>"),
ColumnType::Boolean => format_ident!("bool"),
};
| ColumnType::Custom(_) => "String",
ColumnType::TinyInteger(_) => "i8",
ColumnType::SmallInteger(_) => "i16",
ColumnType::Integer(_) => "i32",
ColumnType::BigInteger(_) => "i64",
ColumnType::Float(_) | ColumnType::Decimal(_) | ColumnType::Money(_) => "f32",
ColumnType::Double(_) => "f64",
ColumnType::Binary(_) => "Vec<u8>",
ColumnType::Boolean => "bool",
}
.parse()
.unwrap();
match self.not_null {
true => quote! { #ident },
false => quote! { Option<#ident> },
@ -102,6 +102,12 @@ impl Column {
}
}
impl From<ColumnDef> for Column {
fn from(col_def: ColumnDef) -> Self {
(&col_def).into()
}
}
impl From<&ColumnDef> for Column {
fn from(col_def: &ColumnDef) -> Self {
let name = col_def.get_column_name();
@ -145,3 +151,164 @@ impl From<&ColumnDef> for Column {
}
}
}
#[cfg(test)]
mod tests {
use crate::Column;
use proc_macro2::TokenStream;
use quote::quote;
use sea_query::{Alias, ColumnDef, ColumnType, SeaRc};
fn setup() -> Vec<Column> {
macro_rules! make_col {
($name:expr, $col_type:expr) => {
Column {
name: $name.to_owned(),
col_type: $col_type,
auto_increment: false,
not_null: false,
unique: false,
}
};
}
vec![
make_col!("id", ColumnType::String(Some(255))),
make_col!(
"cake_id",
ColumnType::Custom(SeaRc::new(Alias::new("cus_col")))
),
make_col!("CakeId", ColumnType::TinyInteger(None)),
make_col!("CakeId", ColumnType::SmallInteger(None)),
make_col!("CakeId", ColumnType::Integer(Some(11))),
make_col!("CakeFillingId", ColumnType::BigInteger(None)),
make_col!("cake-filling-id", ColumnType::Float(None)),
make_col!("CAKE_FILLING_ID", ColumnType::Double(None)),
make_col!("CAKE-FILLING-ID", ColumnType::Binary(None)),
make_col!("CAKE", ColumnType::Boolean),
]
}
#[test]
fn test_get_name_snake_case() {
let columns = setup();
let snack_cases = vec![
"id",
"cake_id",
"cake_id",
"cake_id",
"cake_id",
"cake_filling_id",
"cake_filling_id",
"cake_filling_id",
"cake_filling_id",
"cake",
];
for (col, snack_case) in columns.into_iter().zip(snack_cases) {
assert_eq!(col.get_name_snake_case().to_string(), snack_case);
}
}
#[test]
fn test_get_name_camel_case() {
let columns = setup();
let camel_cases = vec![
"Id",
"CakeId",
"CakeId",
"CakeId",
"CakeId",
"CakeFillingId",
"CakeFillingId",
"CakeFillingId",
"CakeFillingId",
"Cake",
];
for (col, camel_case) in columns.into_iter().zip(camel_cases) {
assert_eq!(col.get_name_camel_case().to_string(), camel_case);
}
}
#[test]
fn test_get_rs_type() {
let columns = setup();
let rs_types = vec![
"String", "String", "i8", "i16", "i32", "i64", "f32", "f64", "Vec<u8>", "bool",
];
for (mut col, rs_type) in columns.into_iter().zip(rs_types) {
let rs_type: TokenStream = rs_type.parse().unwrap();
col.not_null = true;
assert_eq!(col.get_rs_type().to_string(), quote!(#rs_type).to_string());
col.not_null = false;
assert_eq!(
col.get_rs_type().to_string(),
quote!(Option<#rs_type>).to_string()
);
}
}
#[test]
fn test_get_def() {
let columns = setup();
let col_defs = vec![
"ColumnType::String(Some(255u32)).def()",
"ColumnType::Custom(\"cus_col\".to_owned()).def()",
"ColumnType::TinyInteger.def()",
"ColumnType::SmallInteger.def()",
"ColumnType::Integer.def()",
"ColumnType::BigInteger.def()",
"ColumnType::Float.def()",
"ColumnType::Double.def()",
"ColumnType::Binary.def()",
"ColumnType::Boolean.def()",
];
for (mut col, col_def) in columns.into_iter().zip(col_defs) {
let mut col_def: TokenStream = col_def.parse().unwrap();
col.not_null = true;
assert_eq!(col.get_def().to_string(), col_def.to_string());
col.not_null = false;
col_def.extend(quote!(.null()));
assert_eq!(col.get_def().to_string(), col_def.to_string());
col.unique = true;
col_def.extend(quote!(.unique()));
assert_eq!(col.get_def().to_string(), col_def.to_string());
}
}
#[test]
fn test_from_column_def() {
let column: Column = ColumnDef::new(Alias::new("id")).string().into();
assert_eq!(
column.get_def().to_string(),
quote! {
ColumnType::String(None).def().null()
}
.to_string()
);
let column: Column = ColumnDef::new(Alias::new("id")).string().not_null().into();
assert!(column.not_null);
let column: Column = ColumnDef::new(Alias::new("id"))
.string()
.unique_key()
.not_null()
.into();
assert!(column.unique);
assert!(column.not_null);
let column: Column = ColumnDef::new(Alias::new("id"))
.string()
.auto_increment()
.unique_key()
.not_null()
.into();
assert!(column.auto_increment);
assert!(column.unique);
assert!(column.not_null);
}
}

View File

@ -111,15 +111,242 @@ impl Entity {
.collect()
}
pub fn get_relation_rel_find_helpers(&self) -> Vec<Ident> {
self.relations
.iter()
.map(|rel| rel.get_rel_find_helper())
.collect()
}
pub fn get_primary_key_auto_increment(&self) -> Ident {
let auto_increment = self.columns.iter().any(|col| col.auto_increment);
format_ident!("{}", auto_increment)
}
}
#[cfg(test)]
mod tests {
use crate::{Column, Entity, PrimaryKey, Relation, RelationType};
use quote::format_ident;
use sea_query::ColumnType;
fn setup() -> Entity {
Entity {
table_name: "special_cake".to_owned(),
columns: vec![
Column {
name: "id".to_owned(),
col_type: ColumnType::String(None),
auto_increment: false,
not_null: false,
unique: false,
},
Column {
name: "name".to_owned(),
col_type: ColumnType::String(None),
auto_increment: false,
not_null: false,
unique: false,
},
],
relations: vec![
Relation {
ref_table: "fruit".to_owned(),
columns: vec!["id".to_owned()],
ref_columns: vec!["cake_id".to_owned()],
rel_type: RelationType::HasOne,
},
Relation {
ref_table: "filling".to_owned(),
columns: vec!["id".to_owned()],
ref_columns: vec!["cake_id".to_owned()],
rel_type: RelationType::HasOne,
},
],
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
}
}
#[test]
fn test_get_table_name_snake_case() {
let entity = setup();
assert_eq!(
entity.get_table_name_snake_case(),
"special_cake".to_owned()
);
}
#[test]
fn test_get_table_name_camel_case() {
let entity = setup();
assert_eq!(entity.get_table_name_camel_case(), "SpecialCake".to_owned());
}
#[test]
fn test_get_table_name_snake_case_ident() {
let entity = setup();
assert_eq!(
entity.get_table_name_snake_case_ident(),
format_ident!("{}", "special_cake")
);
}
#[test]
fn test_get_table_name_camel_case_ident() {
let entity = setup();
assert_eq!(
entity.get_table_name_camel_case_ident(),
format_ident!("{}", "SpecialCake")
);
}
#[test]
fn test_get_column_names_snake_case() {
let entity = setup();
for (i, elem) in entity.get_column_names_snake_case().into_iter().enumerate() {
assert_eq!(elem, entity.columns[i].get_name_snake_case());
}
}
#[test]
fn test_get_column_names_camel_case() {
let entity = setup();
for (i, elem) in entity.get_column_names_camel_case().into_iter().enumerate() {
assert_eq!(elem, entity.columns[i].get_name_camel_case());
}
}
#[test]
fn test_get_column_rs_types() {
let entity = setup();
for (i, elem) in entity.get_column_rs_types().into_iter().enumerate() {
assert_eq!(
elem.to_string(),
entity.columns[i].get_rs_type().to_string()
);
}
}
#[test]
fn test_get_column_defs() {
let entity = setup();
for (i, elem) in entity.get_column_defs().into_iter().enumerate() {
assert_eq!(elem.to_string(), entity.columns[i].get_def().to_string());
}
}
#[test]
fn test_get_primary_key_names_snake_case() {
let entity = setup();
for (i, elem) in entity
.get_primary_key_names_snake_case()
.into_iter()
.enumerate()
{
assert_eq!(elem, entity.primary_keys[i].get_name_snake_case());
}
}
#[test]
fn test_get_primary_key_names_camel_case() {
let entity = setup();
for (i, elem) in entity
.get_primary_key_names_camel_case()
.into_iter()
.enumerate()
{
assert_eq!(elem, entity.primary_keys[i].get_name_camel_case());
}
}
#[test]
fn test_get_relation_ref_tables_snake_case() {
let entity = setup();
for (i, elem) in entity
.get_relation_ref_tables_snake_case()
.into_iter()
.enumerate()
{
assert_eq!(elem, entity.relations[i].get_ref_table_snake_case());
}
}
#[test]
fn test_get_relation_ref_tables_camel_case() {
let entity = setup();
for (i, elem) in entity
.get_relation_ref_tables_camel_case()
.into_iter()
.enumerate()
{
assert_eq!(elem, entity.relations[i].get_ref_table_camel_case());
}
}
#[test]
fn test_get_relation_defs() {
let entity = setup();
for (i, elem) in entity.get_relation_defs().into_iter().enumerate() {
assert_eq!(elem.to_string(), entity.relations[i].get_def().to_string());
}
}
#[test]
fn test_get_relation_rel_types() {
let entity = setup();
for (i, elem) in entity.get_relation_rel_types().into_iter().enumerate() {
assert_eq!(elem, entity.relations[i].get_rel_type());
}
}
#[test]
fn test_get_relation_columns_camel_case() {
let entity = setup();
for (i, elem) in entity
.get_relation_columns_camel_case()
.into_iter()
.enumerate()
{
assert_eq!(elem, entity.relations[i].get_column_camel_case());
}
}
#[test]
fn test_get_relation_ref_columns_camel_case() {
let entity = setup();
for (i, elem) in entity
.get_relation_ref_columns_camel_case()
.into_iter()
.enumerate()
{
assert_eq!(elem, entity.relations[i].get_ref_column_camel_case());
}
}
#[test]
fn test_get_primary_key_auto_increment() {
let mut entity = setup();
assert_eq!(
entity.get_primary_key_auto_increment(),
format_ident!("{}", false)
);
entity.columns[0].auto_increment = true;
assert_eq!(
entity.get_primary_key_auto_increment(),
format_ident!("{}", true)
);
}
}

View File

@ -16,3 +16,28 @@ impl PrimaryKey {
format_ident!("{}", self.name.to_camel_case())
}
}
#[cfg(test)]
mod tests {
use crate::PrimaryKey;
fn setup() -> PrimaryKey {
PrimaryKey {
name: "cake_id".to_owned(),
}
}
#[test]
fn test_get_name_snake_case() {
let primary_key = setup();
assert_eq!(primary_key.get_name_snake_case(), "cake_id".to_owned());
}
#[test]
fn test_get_name_camel_case() {
let primary_key = setup();
assert_eq!(primary_key.get_name_camel_case(), "CakeId".to_owned());
}
}

View File

@ -64,10 +64,6 @@ impl Relation {
pub fn get_ref_column_camel_case(&self) -> Ident {
format_ident!("{}", self.ref_columns[0].to_camel_case())
}
pub fn get_rel_find_helper(&self) -> Ident {
format_ident!("find_{}", self.ref_table.to_snake_case())
}
}
impl From<&TableForeignKey> for Relation {
@ -87,3 +83,95 @@ impl From<&TableForeignKey> for Relation {
}
}
}
#[cfg(test)]
mod tests {
use crate::{Relation, RelationType};
use proc_macro2::TokenStream;
fn setup() -> Vec<Relation> {
vec![
Relation {
ref_table: "fruit".to_owned(),
columns: vec!["id".to_owned()],
ref_columns: vec!["cake_id".to_owned()],
rel_type: RelationType::HasOne,
},
Relation {
ref_table: "filling".to_owned(),
columns: vec!["filling_id".to_owned()],
ref_columns: vec!["id".to_owned()],
rel_type: RelationType::BelongsTo,
},
Relation {
ref_table: "filling".to_owned(),
columns: vec!["filling_id".to_owned()],
ref_columns: vec!["id".to_owned()],
rel_type: RelationType::HasMany,
},
]
}
#[test]
fn test_get_ref_table_snake_case() {
let relations = setup();
let snake_cases = vec!["fruit", "filling", "filling"];
for (rel, snake_case) in relations.into_iter().zip(snake_cases) {
assert_eq!(rel.get_ref_table_snake_case().to_string(), snake_case);
}
}
#[test]
fn test_get_ref_table_camel_case() {
let relations = setup();
let camel_cases = vec!["Fruit", "Filling", "Filling"];
for (rel, camel_case) in relations.into_iter().zip(camel_cases) {
assert_eq!(rel.get_ref_table_camel_case().to_string(), camel_case);
}
}
#[test]
fn test_get_def() {
let relations = setup();
let rel_defs = vec![
"Entity::has_one(super::fruit::Entity).into()",
"Entity::belongs_to(super::filling::Entity) \
.from(Column::FillingId) \
.to(super::filling::Column::Id) \
.into()",
"Entity::has_many(super::filling::Entity).into()",
];
for (rel, rel_def) in relations.into_iter().zip(rel_defs) {
let rel_def: TokenStream = rel_def.parse().unwrap();
assert_eq!(rel.get_def().to_string(), rel_def.to_string());
}
}
#[test]
fn test_get_rel_type() {
let relations = setup();
let rel_types = vec!["has_one", "belongs_to", "has_many"];
for (rel, rel_type) in relations.into_iter().zip(rel_types) {
assert_eq!(rel.get_rel_type(), rel_type);
}
}
#[test]
fn test_get_column_camel_case() {
let relations = setup();
let cols = vec!["Id", "FillingId", "FillingId"];
for (rel, col) in relations.into_iter().zip(cols) {
assert_eq!(rel.get_column_camel_case(), col);
}
}
#[test]
fn test_get_ref_column_camel_case() {
let relations = setup();
let ref_cols = vec!["CakeId", "Id", "Id"];
for (rel, ref_col) in relations.into_iter().zip(ref_cols) {
assert_eq!(rel.get_ref_column_camel_case(), ref_col);
}
}
}

View File

@ -98,6 +98,7 @@ impl EntityTransformer {
}
}
}
println!("{:#?}", entities);
Ok(EntityWriter { entities })
}
}

View File

@ -150,7 +150,7 @@ impl EntityWriter {
quote! {
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
#(pub #column_names_snake_case: #column_rs_types),*
#(pub #column_names_snake_case: #column_rs_types,)*
}
}
}
@ -160,7 +160,7 @@ impl EntityWriter {
quote! {
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
#(#column_names_camel_case),*
#(#column_names_camel_case,)*
}
}
}
@ -170,7 +170,7 @@ impl EntityWriter {
quote! {
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
#(#primary_key_names_camel_case),*
#(#primary_key_names_camel_case,)*
}
}
}
@ -191,7 +191,7 @@ impl EntityWriter {
quote! {
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
#(#relation_ref_tables_camel_case),*
#(#relation_ref_tables_camel_case,)*
}
}
}
@ -205,7 +205,7 @@ impl EntityWriter {
fn def(&self) -> ColumnDef {
match self {
#(Self::#column_names_camel_case => #column_defs),*
#(Self::#column_names_camel_case => #column_defs,)*
}
}
}
@ -221,7 +221,7 @@ impl EntityWriter {
}
} else {
quote! {
#(Self::#relation_ref_tables_camel_case => #relation_defs),*
#(Self::#relation_ref_tables_camel_case => #relation_defs,)*
}
};
quote! {
@ -274,3 +274,239 @@ impl EntityWriter {
}
}
}
#[cfg(test)]
mod tests {
use crate::{Column, Entity, EntityWriter, PrimaryKey, Relation, RelationType};
use proc_macro2::TokenStream;
use sea_query::ColumnType;
use std::io::{self, BufRead, BufReader};
const ENTITY_FILES: [&'static str; 5] = [
include_str!("../../tests/entity/cake.rs"),
include_str!("../../tests/entity/cake_filling.rs"),
include_str!("../../tests/entity/filling.rs"),
include_str!("../../tests/entity/fruit.rs"),
include_str!("../../tests/entity/vendor.rs"),
];
fn setup() -> Vec<Entity> {
vec![
Entity {
table_name: "cake".to_owned(),
columns: vec![
Column {
name: "id".to_owned(),
col_type: ColumnType::Integer(Some(11)),
auto_increment: true,
not_null: true,
unique: false,
},
Column {
name: "name".to_owned(),
col_type: ColumnType::String(Some(255)),
auto_increment: false,
not_null: true,
unique: false,
},
],
relations: vec![
Relation {
ref_table: "cake_filling".to_owned(),
columns: vec![],
ref_columns: vec![],
rel_type: RelationType::HasMany,
},
Relation {
ref_table: "fruit".to_owned(),
columns: vec![],
ref_columns: vec![],
rel_type: RelationType::HasMany,
},
],
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
},
Entity {
table_name: "cake_filling".to_owned(),
columns: vec![
Column {
name: "cake_id".to_owned(),
col_type: ColumnType::Integer(Some(11)),
auto_increment: false,
not_null: true,
unique: false,
},
Column {
name: "filling_id".to_owned(),
col_type: ColumnType::Integer(Some(11)),
auto_increment: false,
not_null: true,
unique: false,
},
],
relations: vec![
Relation {
ref_table: "cake".to_owned(),
columns: vec!["cake_id".to_owned()],
ref_columns: vec!["id".to_owned()],
rel_type: RelationType::BelongsTo,
},
Relation {
ref_table: "filling".to_owned(),
columns: vec!["filling_id".to_owned()],
ref_columns: vec!["id".to_owned()],
rel_type: RelationType::BelongsTo,
},
],
primary_keys: vec![
PrimaryKey {
name: "cake_id".to_owned(),
},
PrimaryKey {
name: "filling_id".to_owned(),
},
],
},
Entity {
table_name: "filling".to_owned(),
columns: vec![
Column {
name: "id".to_owned(),
col_type: ColumnType::Integer(Some(11)),
auto_increment: true,
not_null: true,
unique: false,
},
Column {
name: "name".to_owned(),
col_type: ColumnType::String(Some(255)),
auto_increment: false,
not_null: true,
unique: false,
},
],
relations: vec![Relation {
ref_table: "cake_filling".to_owned(),
columns: vec![],
ref_columns: vec![],
rel_type: RelationType::HasMany,
}],
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
},
Entity {
table_name: "fruit".to_owned(),
columns: vec![
Column {
name: "id".to_owned(),
col_type: ColumnType::Integer(Some(11)),
auto_increment: true,
not_null: true,
unique: false,
},
Column {
name: "name".to_owned(),
col_type: ColumnType::String(Some(255)),
auto_increment: false,
not_null: true,
unique: false,
},
Column {
name: "cake_id".to_owned(),
col_type: ColumnType::Integer(Some(11)),
auto_increment: false,
not_null: false,
unique: false,
},
],
relations: vec![
Relation {
ref_table: "cake".to_owned(),
columns: vec!["cake_id".to_owned()],
ref_columns: vec!["id".to_owned()],
rel_type: RelationType::BelongsTo,
},
Relation {
ref_table: "vendor".to_owned(),
columns: vec![],
ref_columns: vec![],
rel_type: RelationType::HasMany,
},
],
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
},
Entity {
table_name: "vendor".to_owned(),
columns: vec![
Column {
name: "id".to_owned(),
col_type: ColumnType::Integer(Some(11)),
auto_increment: true,
not_null: true,
unique: false,
},
Column {
name: "name".to_owned(),
col_type: ColumnType::String(Some(255)),
auto_increment: false,
not_null: true,
unique: false,
},
Column {
name: "fruit_id".to_owned(),
col_type: ColumnType::Integer(Some(11)),
auto_increment: false,
not_null: false,
unique: false,
},
],
relations: vec![Relation {
ref_table: "fruit".to_owned(),
columns: vec!["fruit_id".to_owned()],
ref_columns: vec!["id".to_owned()],
rel_type: RelationType::BelongsTo,
}],
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
},
]
}
#[test]
fn test_gen_code_blocks() -> io::Result<()> {
let entities = setup();
assert_eq!(entities.len(), ENTITY_FILES.len());
for (i, entity) in entities.iter().enumerate() {
let mut reader = BufReader::new(ENTITY_FILES[i].as_bytes());
let mut lines: Vec<String> = Vec::new();
reader.read_until(b';', &mut Vec::new())?;
let mut line = String::new();
while reader.read_line(&mut line)? > 0 {
lines.push(line.to_owned());
line.clear();
}
let content = lines.join("");
let expected: TokenStream = content.parse().unwrap();
let generated = EntityWriter::gen_code_blocks(entity)
.into_iter()
.skip(1)
.fold(TokenStream::new(), |mut acc, tok| {
acc.extend(tok);
acc
});
assert_eq!(expected.to_string(), generated.to_string());
}
Ok(())
}
}

View File

@ -0,0 +1,74 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cake"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub name: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
CakeFilling,
Fruit,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(Some(255u32)).def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
}
}
}
impl Related<super::cake_filling::Entity> for Entity {
fn to() -> RelationDef {
Relation::CakeFilling.def()
}
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,81 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cake_filling"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub cake_id: i32,
pub filling_id: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
CakeId,
FillingId,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
CakeId,
FillingId,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
false
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Cake,
Filling,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::CakeId => ColumnType::Integer.def(),
Self::FillingId => ColumnType::Integer.def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Cake => Entity::belongs_to(super::cake::Entity)
.from(Column::CakeId)
.to(super::cake::Column::Id)
.into(),
Self::Filling => Entity::belongs_to(super::filling::Entity)
.from(Column::FillingId)
.to(super::filling::Column::Id)
.into(),
}
}
}
impl Related<super::cake::Entity> for Entity {
fn to() -> RelationDef {
Relation::Cake.def()
}
}
impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
Relation::Filling.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,66 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"filling"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub name: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
CakeFilling,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(Some(255u32)).def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
}
}
}
impl Related<super::cake_filling::Entity> for Entity {
fn to() -> RelationDef {
Relation::CakeFilling.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,80 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"fruit"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub name: String,
pub cake_id: Option<i32> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
CakeId,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Cake,
Vendor,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(Some(255u32)).def(),
Self::CakeId => ColumnType::Integer.def().null(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Cake => Entity::belongs_to(super::cake::Entity)
.from(Column::CakeId)
.to(super::cake::Column::Id)
.into(),
Self::Vendor => Entity::has_many(super::vendor::Entity).into(),
}
}
}
impl Related<super::cake::Entity> for Entity {
fn to() -> RelationDef {
Relation::Cake.def()
}
}
impl Related<super::vendor::Entity> for Entity {
fn to() -> RelationDef {
Relation::Vendor.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,7 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
pub mod cake;
pub mod cake_filling;
pub mod filling;
pub mod fruit;
pub mod vendor;

View File

@ -0,0 +1,7 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
pub use super::cake::Entity as Cake;
pub use super::cake_filling::Entity as CakeFilling;
pub use super::filling::Entity as Filling;
pub use super::fruit::Entity as Fruit;
pub use super::vendor::Entity as Vendor;

View File

@ -0,0 +1,72 @@
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"vendor"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub id: i32,
pub name: String,
pub fruit_id: Option<i32> ,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Name,
FruitId,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Fruit,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(Some(255u32)).def(),
Self::FruitId => ColumnType::Integer.def().null(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Fruit => Entity::belongs_to(super::fruit::Entity)
.from(Column::FruitId)
.to(super::fruit::Column::Id)
.into(),
}
}
}
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
Relation::Fruit.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,127 @@
mod entity;
use entity::*;
use sea_orm::{entity::*, error::*, MockDatabase, MockExecResult, Transaction};
#[async_std::test]
async fn test_insert() -> Result<(), DbErr> {
let exec_result = MockExecResult {
last_insert_id: 1,
rows_affected: 1,
};
let db = MockDatabase::new()
.append_exec_results(vec![exec_result.clone()])
.into_connection();
let apple = cake::ActiveModel {
name: Set("Apple Pie".to_owned()),
..Default::default()
};
let insert_result = cake::Entity::insert(apple).exec(&db).await?;
assert_eq!(insert_result.last_insert_id, exec_result.last_insert_id);
assert_eq!(
db.into_transaction_log(),
vec![Transaction::from_sql_and_values(
r#"INSERT INTO "cake" ("name") VALUES ($1)"#,
vec!["Apple Pie".into()]
)]
);
Ok(())
}
#[async_std::test]
async fn test_select() -> Result<(), DbErr> {
let query_results = vec![cake_filling::Model {
cake_id: 2,
filling_id: 3,
}];
let db = MockDatabase::new()
.append_query_results(vec![query_results.clone()])
.into_connection();
let selected_models = cake_filling::Entity::find_by_id((2, 3)).all(&db).await?;
assert_eq!(selected_models, query_results);
assert_eq!(
db.into_transaction_log(),
vec![Transaction::from_sql_and_values([
r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
r#"WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
].join(" ").as_str(),
vec![2i32.into(), 3i32.into()]
)]
);
Ok(())
}
#[async_std::test]
async fn test_update() -> Result<(), DbErr> {
let exec_result = MockExecResult {
last_insert_id: 1,
rows_affected: 1,
};
let db = MockDatabase::new()
.append_exec_results(vec![exec_result.clone()])
.into_connection();
let orange = fruit::ActiveModel {
id: Set(1),
name: Set("Orange".to_owned()),
..Default::default()
};
let updated_model = fruit::Entity::update(orange.clone()).exec(&db).await?;
assert_eq!(updated_model, orange);
assert_eq!(
db.into_transaction_log(),
vec![Transaction::from_sql_and_values(
r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2"#,
vec!["Orange".into(), 1i32.into()]
)]
);
Ok(())
}
#[async_std::test]
async fn test_delete() -> Result<(), DbErr> {
let exec_result = MockExecResult {
last_insert_id: 1,
rows_affected: 1,
};
let db = MockDatabase::new()
.append_exec_results(vec![exec_result.clone()])
.into_connection();
let orange = fruit::ActiveModel {
id: Set(3),
..Default::default()
};
let delete_result = fruit::Entity::delete(orange).exec(&db).await?;
assert_eq!(delete_result.rows_affected, exec_result.rows_affected);
assert_eq!(
db.into_transaction_log(),
vec![Transaction::from_sql_and_values(
r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
vec![3i32.into()]
)]
);
Ok(())
}