From 6c54b2012e7a90a812833a9f9fe0812422419b00 Mon Sep 17 00:00:00 2001 From: peefy Date: Mon, 9 Sep 2024 21:07:19 +0800 Subject: [PATCH] test: add more wasm example tests Signed-off-by: peefy --- wasm/examples/rust/src/tests.rs | 208 +++++++++++++++++++++++++ wasm/tests/run.test.ts | 268 ++++++++++++++++++++++++++++++++ 2 files changed, 476 insertions(+) diff --git a/wasm/examples/rust/src/tests.rs b/wasm/examples/rust/src/tests.rs index 54ceae45..e9a0b6d4 100644 --- a/wasm/examples/rust/src/tests.rs +++ b/wasm/examples/rust/src/tests.rs @@ -3,6 +3,200 @@ use anyhow::Result; const WASM_PATH: &str = "../../kcl.wasm"; const BENCH_COUNT: usize = 20; +const SOURCES: &[&str] = &[ + r#"apiVersion = "apps/v1" +kind = "Deployment" +metadata = { + name = "nginx" + labels.app = "nginx" +} +spec = { + replicas = 3 + selector.matchLabels = metadata.labels + template.metadata.labels = metadata.labels + template.spec.containers = [ + { + name = metadata.name + image = "nginx:1.14.2" + ports = [{ containerPort = 80 }] + } + ] +}"#, + r#"import manifests + +schema App: + """The application model.""" + name: str + replicas: int = 1 + labels?: {str:str} = {app = name} + service?: Service + containers?: {str:Container} + +schema Service: + """The service model.""" + $type?: str + ports: [Port] + +schema Port: + """The port model.""" + port: int + protocol: "TCP" | "UDP" | "SCTP" = "TCP" + targetPort?: int | str + +schema Container: + """The container model.""" + image: str + command?: [str] + args?: [str] + env?: [Env] + volumes?: [Volume] + resources?: Resource + ports: [ContainerPort] + +schema ContainerPort: + """The container port model.""" + name?: str + protocol: "TCP" | "UDP" | "SCTP" = "TCP" + containerPort: int + + check: + 1 <= containerPort <= 65535, "containerPort must be between 1 and 65535, inclusive" + +schema Env: + name: str + value: str + +schema Volume: + source: str + path: str + target: str + readOnly?: bool = False + +schema Resource: + limits?: {str:} + requests?: {str:} + +kubernetesRender = lambda a: App { + # Construct the deployment manifest. + deployment = { + apiVersion = "apps/v1" + kind = "Deployment" + metadata.name = a.name + metadata.labels = a.labels + spec = { + replicas = a.replicas + selector.matchLabels = a.labels + template.metadata.labels = a.labels + template.spec.containers = [{ + name = name + image = c.image + command = c.command + args = c.args + env = c.env + volumeMounts = c.volumes + resources: c.resources + ports = c.ports + } for name, c in a.containers] + } + } + # Construct the service manifest. + service = { + apiVersion = "v1" + kind = "Service" + metadata.name = a.name + metadata.labels = a.labels + spec = { + type = a.service?.$type + selector = a.labels + ports = a.service?.ports + } + } + # Returns Kubernetes manifests + [ + deployment + if a.service: + service + + ] +} + +app = App { + name = "app" + containers.nginx = { + image = "nginx" + ports = [{containerPort = 80}] + } + service.ports = [{port = 80}] +} + +manifests.yaml_stream(sum([kubernetesRender(a) for a in App.instances()], []))"#, + r#"import yaml + +resource = yaml.decode("""\ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 +""") + +set_replicas = lambda item: {str:}, replicas: int { + item | { + if item?.kind == "Deployment": + spec.replicas = replicas + + } +} + +new_resource = set_replicas(resource, 5)"#, + r#"import yaml +import json + +schema Server: + ports: [int] + + check: + all p in ports { + 0 < p < 65535 + } + +server1: Server = yaml.decode("""\ +ports: +- 80 +- 8080 +""") +server2: Server = json.decode("""\ +{ + "ports": [80, 8000] +} +""")"#, + r#"x = (10 + 2) * 30 + 5"#, + r#"print("Output values")"#, + r#"if True: + print("Hello") +else: + print("unreachable")"#, + r#"data = ["one", "two", "three"]"#, + r#"Config = { + key = "value" +}"#, +]; #[test] fn test_run() -> Result<()> { @@ -18,6 +212,20 @@ fn test_run() -> Result<()> { Ok(()) } +#[test] +fn test_run_examples() -> Result<()> { + for source in SOURCES { + let opts = RunOptions { + filename: "test.k".to_string(), + source: source.to_string(), + }; + let mut module = KCLModule::from_path(WASM_PATH)?; + let result = module.run(&opts)?; + assert!(!result.starts_with("ERROR:"), "source: {source}. result: {result}"); + } + Ok(()) +} + #[test] fn test_run_parse_error() -> Result<()> { let opts = RunOptions { diff --git a/wasm/tests/run.test.ts b/wasm/tests/run.test.ts index fdf29588..3f3f6b6e 100644 --- a/wasm/tests/run.test.ts +++ b/wasm/tests/run.test.ts @@ -13,3 +13,271 @@ p = Person {name = "Alice"}`, }); expect(result).toBe("p:\n name: Alice"); }); + +export const SNIPPETS = [ + { + label: "Kubernetes", + value: `apiVersion = "apps/v1" +kind = "Deployment" +metadata = { + name = "nginx" + labels.app = "nginx" +} +spec = { + replicas = 3 + selector.matchLabels = metadata.labels + template.metadata.labels = metadata.labels + template.spec.containers = [ + { + name = metadata.name + image = "nginx:1.14.2" + ports = [{ containerPort = 80 }] + } + ] +} +`, + }, + { + label: "Abstraction", + value: `import manifests + +schema App: + """The application model.""" + name: str + replicas: int = 1 + labels?: {str:str} = {app = name} + service?: Service + containers?: {str:Container} + +schema Service: + """The service model.""" + $type?: str + ports: [Port] + +schema Port: + """The port model.""" + port: int + protocol: "TCP" | "UDP" | "SCTP" = "TCP" + targetPort?: int | str + +schema Container: + """The container model.""" + image: str + command?: [str] + args?: [str] + env?: [Env] + volumes?: [Volume] + resources?: Resource + ports: [ContainerPort] + +schema ContainerPort: + """The container port model.""" + name?: str + protocol: "TCP" | "UDP" | "SCTP" = "TCP" + containerPort: int + + check: + 1 <= containerPort <= 65535, "containerPort must be between 1 and 65535, inclusive" + +schema Env: + name: str + value: str + +schema Volume: + source: str + path: str + target: str + readOnly?: bool = False + +schema Resource: + limits?: {str:} + requests?: {str:} + +kubernetesRender = lambda a: App { + # Construct the deployment manifest. + deployment = { + apiVersion = "apps/v1" + kind = "Deployment" + metadata.name = a.name + metadata.labels = a.labels + spec = { + replicas = a.replicas + selector.matchLabels = a.labels + template.metadata.labels = a.labels + template.spec.containers = [{ + name = name + image = c.image + command = c.command + args = c.args + env = c.env + volumeMounts = c.volumes + resources: c.resources + ports = c.ports + } for name, c in a.containers] + } + } + # Construct the service manifest. + service = { + apiVersion = "v1" + kind = "Service" + metadata.name = a.name + metadata.labels = a.labels + spec = { + type = a.service?.$type + selector = a.labels + ports = a.service?.ports + } + } + # Returns Kubernetes manifests + [ + deployment + if a.service: + service + + ] +} + +app = App { + name = "app" + containers.nginx = { + image = "nginx" + ports = [{containerPort = 80}] + } + service.ports = [{port = 80}] +} + +manifests.yaml_stream(sum([kubernetesRender(a) for a in App.instances()], [])) +`, + }, + { + label: "Mutation", + value: `import yaml + +resource = yaml.decode("""\ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 +""") + +set_replicas = lambda item: {str:}, replicas: int { + item | { + if item?.kind == "Deployment": + spec.replicas = replicas + + } +} + +new_resource = set_replicas(resource, 5) +`, + }, + { + label: "Validation", + value: `import yaml +import json + +schema Server: + ports: [int] + + check: + all p in ports { + 0 < p < 65535 + } + +server1: Server = yaml.decode("""\ +ports: +- 80 +- 8080 +""") +server2: Server = json.decode("""\ +{ + "ports": [80, 8000] +} +""") +`, + }, + { + label: "Computing integers", + value: `x = (10 + 2) * 30 + 5 +`, + }, + { + label: "Debugging values", + value: `print("Output values") +`, + }, + { + label: "Conditionals", + value: `if True: + print("Hello") +else: + print("unreachable") +`, + }, + { + label: "List", + value: `data = ["one", "two", "three"]`, + }, + { + label: "Dict", + value: `Config = { + key = "value" +}`, + }, + { + label: "Schema", + value: `schema Nginx: + """Schema for Nginx configuration files""" + http: Http + +schema Http: + server: Server + +schema Server: + listen: int | str # The attribute listen can be int type or a string type. + location?: Location # Optional, but must be non-empty when specified + +schema Location: + root: str + index: str + +nginx = Nginx { + http.server = { + listen = 80 + location = { + root = "/var/www/html" + index = "index.html" + } + } +}`, + }, +]; + +test("run example tests", async () => { + SNIPPETS.forEach((snippet) => { + load().then((inst) => { + const result = invokeKCLRun(inst, { + filename: "test.k", + source: snippet.value, + }); + expect(result).not.toContain("ERROR:"); + }); + }); +});