Skip to content

Commit

Permalink
enable auto generation of tests
Browse files Browse the repository at this point in the history
  • Loading branch information
wkarwacki committed Aug 19, 2024
1 parent f4d5a65 commit b7603b5
Show file tree
Hide file tree
Showing 19 changed files with 193 additions and 66 deletions.
16 changes: 6 additions & 10 deletions src/lib/gen/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use dyn_clone::DynClone;
use filter::{FilterNonconst, FilterOpParamsByLoc};
use fmt::{FmtClass, FmtEnum, FmtName, FmtOpt, FmtSrcIfPresent, FmtType, FmtValue};
use handlebars::Handlebars;
use proc::{Parents, Resolve, TypeArgs, TypeParams, ValueDef};
use proc::{Parents, Resolve, ResolveIfRef, TypeArgs, TypeParams, ValueDef};
use prop::{HasKey, IsAlias};
use r#fn::{Add, Json};
use serde_json::json;
Expand Down Expand Up @@ -73,15 +73,11 @@ pub(crate) fn go(
handlebars.register_helper("hasKey", Box::new(HasKey {}.clone()));

handlebars.register_helper("parents", Box::new(Parents {}.clone()));
handlebars.register_helper(
"resolve",
Box::new(
Resolve {
context: context.clone(),
}
.clone(),
),
);
let resolve = Resolve {
context: context.clone(),
};
handlebars.register_helper("resolve", Box::new(resolve.clone()));
handlebars.register_helper("resolveIfRef", Box::new(ResolveIfRef { resolve }));
handlebars.register_helper("sortOptionalsLast", Box::new(SortOptionalsLast {}.clone()));
handlebars.register_helper("typeArgs", Box::new(TypeArgs {}.clone()));
handlebars.register_helper("toFlatCase", Box::new(ToFlatCase {}));
Expand Down
64 changes: 64 additions & 0 deletions src/lib/gen/python/client/gen_python_http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,28 @@ impl Gen for GenPythonHttpClient {
.as_str()
+ "Base"))
}
Def::Seq(_) =>
names.push(
"from ".to_string()
+ self.lang.module().as_str()
+ "."
+ match &src {
None => self.lang.feature.clone().to_case(Case::Snake),
Some(src) => self.lang.fmt_src(src.as_str()),
}
.as_str()
+ "."
+ r#ref.class_name().to_case(Case::Snake).as_str()
+ " import "
+ dto_name(
self.lang
.fmt_class(r#ref.class_name().as_str(), &None)
.as_str(),
&self.lang(),
)
.as_str()
+ "Item",
),
_ => {}
}
names
Expand Down Expand Up @@ -248,6 +270,27 @@ impl Gen for GenPythonHttpClient {
+ "Base",
)
}),
Def::Seq(_) => names.push(
"from ".to_string()
+ self.lang.module().as_str()
+ "."
+ match &src {
None => self.lang.feature.clone().to_case(Case::Snake),
Some(src) => self.lang.fmt_src(src.as_str()),
}
.as_str()
+ "."
+ r#ref.class_name().to_case(Case::Snake).as_str()
+ " import "
+ dto_name(
self.lang
.fmt_class(r#ref.class_name().as_str(), &None)
.as_str(),
&self.lang(),
)
.as_str()
+ "Item",
),
_ => {}
}
names
Expand Down Expand Up @@ -306,6 +349,27 @@ impl Gen for GenPythonHttpClient {
};
result.insert(service.0, imports.clone() + "\n" + service.1.as_str());

if self.lang.gen_cfg.auto_implement {
let test = {
let test_template = templates.get("test").unwrap();
let test = handlebars
.render_template(
test_template.as_str(),
&json!({"module": self.lang.module(), "feature": self.lang.feature.clone(), "ops": &pkg.ops}),
)
.unwrap();
(
{
let out_dir = self.lang.out_dir().to_string_lossy().to_string();
let test_path = "test_trust.py".to_string();
format!("{out_dir}/{test_path}").into()
},
test,
)
};
result.insert(test.0, imports.clone() + "\n" + test.1.as_str());
}

result
}
}
18 changes: 10 additions & 8 deletions src/lib/gen/python/client/templates/dto.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@
{{else if val.val}}{{>dto key=(add key "Value") val=val.val tl=false prefix=prefix}}

{{/if}}{{#if val.adt}}class {{key}}Base(Dto):
{{indent}} @classmethod
{{indent}} def get(cls) -> '{{fmtClass key}}':
{{indent}} return {{#with prefix}}{{this}}.{{/with}}{{#each val.adt.map}}{{#if @first}}{{../key}}{{fmtClass @key}}.get(){{/if}}{{/each}}{{#each val.vars}}{{#if (or (eq this.type "obj") this.item this.val)}}{{>dto key=(fmtClass @key) val=this indent=(add ../indent " ")}}{{/if}}{{/each}}{{#each val.vars}}
{{indent}} @classmethod
{{indent}} def get(cls) -> '{{fmtClass key}}':
{{indent}} return {{#with prefix}}{{this}}.{{/with}}{{#each val.adt.map}}{{#if @first}}{{../key}}{{fmtClass @key}}.get(){{/if}}{{/each}}{{#each val.vars}}{{#if (or (eq this.type "obj") this.item this.val)}}{{>dto key=(fmtClass @key) val=this indent=(add ../indent " ")}}{{/if}}{{/each}}{{#each val.vars}}
{{indent}}{{#if (eq @key ../val.adt.var)}}{{fmtName @key}}: {{#each../val.adt.map}}Literal[{{json @key}}]{{#unless @last}} | {{/unless}}{{/each}}
{{else}}{{../indent}}{{>var}}{{/if}}{{/each}}
{{#each val.adt.map}}
{{>dto key=(fmtClass @key) val=this adtParent=../key}}
{{>dto key=(fmtClass @key) val=this adtParent=../key adtVar=../val.adt.var}}

{{fmtName ../val.adt.var}}: Literal[{{json @key}}] = {{json @key}}


{{/each}}

{{key}} ={{>union}}{{else if (and (isAlias val) tl)}}{{key}} = {{fmtType val key}}{{else if (or val.vars adtParent)}}class {{adtParent}}{{fmtClass key}}({{#if (parents val)}}{{#each (parents val)}}{{fmtType this}}{{#unless @last}}, {{/unless}}{{/each}}{{else if adtParent}}{{adtParent}}Base{{else}}Dto{{/if}}){{>typeParams val=val}}:
{{key}} ={{>union}} {{else if (and (isAlias val) tl)}}{{key}} = {{fmtType val key}}{{else if (or val.vars adtParent)}}class {{adtParent}}{{fmtClass key}}({{#each (parents val)}}{{#with (resolveIfRef this)}}{{#unless (eq this.type "struct")}}{{>dtoName val=(fmtClass ../path)}}{{#if @last}}{{#if ../adtParent}}, {{/if}}{{else}}, {{/if}}{{else}}{{#if @last}}{{#if ../adtParent}}, {{/if}}{{/if}}{{/unless}}{{/with}}{{/each}}{{#if adtParent}}{{adtParent}}Base{{/if}}{{#unless (or (parents val) adtParent)}}Dto{{/unless}}){{>typeParams val=val}}:
{{indent}} @classmethod
{{indent}} def get(cls) -> '{{adtParent}}{{key}}':
{{indent}} return {{#with prefix}}{{this}}.{{/with}}{{>stubImpl key=(add adtParent key) val=val indent=(add indent " ")}}
{{indent}} return {{#with prefix}}{{this}}.{{/with}}{{>stubImpl key=(add adtParent key) val=val adtVar=adtVar indent=(add indent " ")}}
{{indent}}{{#each val.vars}}{{#if (or (or (eq this.type "obj") (eq this.type "enum")) this.item this.val)}} {{>dto key=(fmtClass @key) val=this dtoFormLike=(or ../formLike ../dtoFormLike) indent=(add ../indent " ") prefix=../key}}{{/if}}{{/each}}{{#each val.vars}}{{#if (or (not ../this.formLike) (not (eq @key "file")))}}
{{../indent}}{{>var}}{{/if}}{{/each}}{{else if (eq val.type "enum")}}class {{key}}({{#with (valueDef val.vars.[0])}}{{#if (eq type "int")}}IntEnum{{else}}str, Enum{{/if}}{{/with}}):{{#each val.vals}}
{{fmtEnum this}} = {{json this}}{{/each}}{{/if}}{{#if (and (and (hasKey this "tl") (not tl)) (and dtoFormLike (eq val.type "obj")))}}
{{../indent}}{{>var}}{{/if}}{{/each}}{{else if (eq val.type "enum")}}class {{key}}({{#with (valueDef val.vars.[0])}}{{#if (eq type "int")}}IntEnum{{else}}str, Enum{{/if}}{{/with}}):
{{#each val.vals}}
{{../indent}}{{fmtEnum this}} = {{json this}}
{{/each}}{{/if}}{{#if (and (and (hasKey this "tl") (not tl)) (and dtoFormLike (eq val.type "obj")))}}

{{../indent}}@model_validator(mode = "after")
{{../indent}}def serialize(cls, {{fmtName key}}: '{{fmtClass key}}') -> str:
Expand Down
2 changes: 1 addition & 1 deletion src/lib/gen/python/client/templates/service.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ class {{to_pascal_case feature}}Service:
{{#if this.res}}response = {{/if}}client.{{to_lower_case this.type}}("{{@../key}}".format({{#each (filterOpParamsByLoc this.params "path")}}{{this.name}} = {{to_snake_case this.name}}{{#unless @last}}, {{/unless}}{{/each}}), params = query_params{{#if this.req}}, {{#if (eq this.req.form "multipart/form-data")}}data = {{#if this.req.path}}{{fmtName (fmtType this.req)}}{{else}}{{fmtName this.name}}{{/if}}.form(), files = {"file": file}{{else}}data = {{#if this.req.path}}{{fmtName (fmtType this.req)}}{{else if (or (eq this.req.type "obj") (eq this.req.type "seq") (eq this.req.type "map"))}}{{fmtName (add this.name "Req")}}{{else}}request{{/if}}.model_dump_json(exclude_none=True){{/if}}{{/if}}){{#if this.res}}
if response.is_error:
raise Exception(response.json())
return parse_obj_as({{#if this.res.path}}{{fmtType this.res (to_pascal_case this.name)}}{{else if (or (eq this.res.type "obj") (eq this.res.type "seq") (eq this.res.type "map"))}}{{>dtoName val=(fmtClass (add this.name "Res"))}}{{else}}{{fmtType this.res}}{{/if}}, response.json()){{/if}}
{{#if this.res}}return parse_obj_as({{#if this.res.path}}{{fmtType this.res (to_pascal_case this.name)}}{{else if (or (eq this.res.type "obj") (eq this.res.type "seq") (eq this.res.type "map"))}}{{>dtoName val=(fmtClass (add this.name "Res"))}}{{else}}{{fmtType this.res}}{{/if}}, response.json()){{/if}}{{/if}}
{{/each}}{{/each}}
7 changes: 4 additions & 3 deletions src/lib/gen/python/client/templates/stubImpl.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{#if (and val.path (not (eq val.type "alias")))}}{{#with (resolve val)}}{{#if (eq this.type "obj")}}{{#if this.adt}}{{>dtoName val=(fmtClass (fmtName ../val.path))}}Base{{else}}{{>dtoName val=(fmtClass ../val.path)}}{{/if}}.get(){{else}}{{>stubImpl val=this indent=indent}}{{/if}}{{/with}}{{else if (or (eq val.type "obj") val.vars)}}{{key}}({{#each val.vars}}
{{../indent}} {{fmtName @key}}={{>stubImpl val=this key=(fmtClass @key) indent=(add ../indent " ") prefix=../key}},{{/each}}
{{indent}}){{else if (eq val.type "map")}}{ {{#if val.key}}{{#if (and prefix (not val.key.path))}}{{prefix}}.{{/if}}{{>stubImpl val=val.key key=(add key "Key") indent=indent}}{{else}}{{>stubImpl val="str" key=(add key "Key") indent=indent}}{{/if}}: {{#if val.val}}{{#if (and prefix (not val.val.path))}}{{prefix}}.{{/if}}{{>stubImpl val=val.val key=(add key "Val") indent=indent}}{{else}}{{>stubImpl val="str" key=(add key "Val") indent=indent}}{{/if}} }{{else if (eq val.type "seq")}}[{{#if (eq val.item.type "obj")}}{{#if prefix}}{{prefix}}.{{/if}}{{add key "Item"}}.get(){{else}}{{>stubImpl val=val.item indent=indent prefix=null}}{{/if}}]{{else if val.mix}}{{unimplemented}}{{else}}{{stubImpl val indent=indent}}{{/if}}
{{#if (and val.path (not (eq val.type "alias")))}}{{#with (resolve val)}}{{#if (eq this.type "obj")}}{{#if this.adt}}{{>dtoName val=(fmtClass (fmtName ../val.path))}}Base{{else}}{{>dtoName val=(fmtClass ../val.path)}}{{/if}}.get(){{else}}{{>stubImpl val=this indent=indent}}{{/if}}{{/with}}{{else if (or (eq val.type "obj") (or val.vars val.mix))}}{{key}}({{#each val.vars}}
{{../indent}} {{fmtName @key}}={{>stubImpl val=this key=(fmtClass @key) indent=(add ../indent " ") prefix=../key}}{{#if (or (not @last) ../val.mix)}},{{/if}}{{/each}}{{#each val.mix}}
{{../indent}} **{{#if ../adtVar}}{ k:v for k, v in {{/if}}{{>dtoName val=(fmtClass this.path)}}.get().dict(){{#if ../adtVar}}.items() if k != {{json (fmtName ../adtVar)}} }{{/if}}{{#unless @last}},{{/unless}}{{/each}}
{{indent}}){{else if (eq val.type "map")}}{ {{#if val.key}}{{#if (eq val.key.type "obj")}}{{#if prefix}}{{prefix}}.{{/if}}{{add key "Key"}}.get(){{else}}{{>stubImpl val=val.item indent=indent prefix=null}}{{/if}}{{else}}{{>stubImpl val="str" key=(add key "Key") indent=indent}}{{/if}}: {{#if val.val}}{{#if (eq val.val.type "obj")}}{{#if prefix}}{{prefix}}.{{/if}}{{add key "Val"}}.get(){{else}}{{>stubImpl val=val.val indent=indent prefix=null}}{{/if}}{{else}}{{>stubImpl val="str" key=(add key "Val") indent=indent}}{{/if}} }{{else if (eq val.type "seq")}}[{{#if (eq val.item.type "obj")}}{{#if prefix}}{{prefix}}.{{/if}}{{#if origin}}{{>dtoName val=(fmtClass origin.path)}}{{/if}}{{add key "Item"}}.get(){{else}}{{>stubImpl val=val.item indent=indent prefix=prefix}}{{/if}}]{{else}}{{stubImpl val indent=indent}}{{/if}}
10 changes: 10 additions & 0 deletions src/lib/gen/python/client/templates/test.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from typing import Any

from {{fmtName module}}.{{fmtName feature}}.service import {{fmtClass feature}}Service

service={{fmtClass ../../feature}}Service()

{{#each ops}}{{#each this}}
def test_{{to_snake_case this.name}}({{#if this.req}}{{#if this.req.path}}{{fmtName (fmtType this.req)}}: {{fmtType this.req (to_pascal_case this.name)}}{{else if (or (eq this.req.type "obj") (eq this.req.type "seq") (eq this.req.type "map"))}}{{fmtName (add this.name "Req")}}: {{>dtoName val=(fmtClass (add this.name "Req"))}}{{else}}request: {{fmtType this.req}}{{/if}}{{#if (eq this.req.form "multipart/form-data")}}, file: BufferedReader{{/if}} = {{>stubImpl val=this.req key="Req"}}{{/if}}{{#if this.params}}{{#if this.req}}, {{/if}}{{#each (sortOptionalsLast this.params)}}{{to_snake_case this.name}}: {{fmtType this}} = {{>stubImpl val=this key=this.name}}{{#unless @last}}, {{/unless}}{{/each}}{{/if}}) -> None:
service.{{to_snake_case this.name}}({{#if this.req}}{{#if this.req.path}}{{fmtName (fmtType this.req)}}{{else if (or (eq this.req.type "obj") (eq this.req.type "seq") (eq this.req.type "map"))}}{{fmtName (add this.name "Req")}}{{else}}request{{/if}}{{#if (eq this.req.form "multipart/form-data")}}, file{{/if}}{{/if}}{{#if this.params}}{{#if this.req}}, {{/if}}{{#each (sortOptionalsLast this.params)}}{{to_snake_case this.name}}{{#unless @last}}, {{/unless}}{{/each}}{{/if}})
{{/each}}{{/each}}
2 changes: 1 addition & 1 deletion src/lib/gen/python/lang_python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl Lang for LangPython {
EnumVals::Str(vals) => "\"".to_string() + vals.first().unwrap() + "\"",
},
Def::Int(_) => "0".to_string(),
Def::Str(_) => "\"\"".to_string(),
Def::Str(_) => "\"str\"".to_string(),
Def::Struct(_) => "{}".to_string(),
_ => unreachable!(),
}
Expand Down
43 changes: 43 additions & 0 deletions src/lib/gen/python/server/gen_python_http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,28 @@ impl Gen for GenPythonHttpServer {
.as_str()
+ "Base"))
}
Def::Seq(_) =>
names.push(
"from ".to_string()
+ self.lang.module().as_str()
+ "."
+ match &src {
None => self.lang.feature.clone().to_case(Case::Snake),
Some(src) => self.lang.fmt_src(src.as_str()),
}
.as_str()
+ "."
+ r#ref.class_name().to_case(Case::Snake).as_str()
+ " import "
+ dto_name(
self.lang
.fmt_class(r#ref.class_name().as_str(), &None)
.as_str(),
&self.lang(),
)
.as_str()
+ "Item",
),
_ => {}
}
names
Expand Down Expand Up @@ -247,6 +269,27 @@ impl Gen for GenPythonHttpServer {
+ "Base",
)
}),
Def::Seq(_) => names.push(
"from ".to_string()
+ self.lang.module().as_str()
+ "."
+ match &src {
None => self.lang.feature.clone().to_case(Case::Snake),
Some(src) => self.lang.fmt_src(src.as_str()),
}
.as_str()
+ "."
+ r#ref.class_name().to_case(Case::Snake).as_str()
+ " import "
+ dto_name(
self.lang
.fmt_class(r#ref.class_name().as_str(), &None)
.as_str(),
&self.lang(),
)
.as_str()
+ "Item",
),
_ => {}
}
names
Expand Down
Loading

0 comments on commit b7603b5

Please sign in to comment.