diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1c6314a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = tab +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0ca3baa --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" + allow: + - dependency-name: "@slangroom/*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c81f099 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,16 @@ +name: ๐Ÿงช & ๐Ÿ“ฃ +on: + - push + - pull_request +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 + with: + bun-version: latest + - run: bun i + - run: make tests diff --git a/Makefile b/Makefile index 65bed85..1211d38 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,30 @@ .PHONY: help +SOURCES = $(shell find src -type f -name '*.ts') +LIBS = node_modules + DEPS = bun K := $(foreach exec,$(DEPS),\ $(if $(shell which $(exec)),some string,$(error "๐Ÿฅถ `$(exec)` not found in PATH please install it"))) -help: ## ๐Ÿ›Ÿ Show this help message - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m ๐Ÿ‘‰๐Ÿป %-7s\033[0m %s\n", $$1, $$2}' +help: ## ๐Ÿ›Ÿ Show this help message + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m ๐Ÿ‘‰ %-14s\033[0m %s\n", $$1, $$2}' -slangroom-exec: index.ts ## ๐Ÿ› ๏ธ Build slangroom-exec - bun build ./index.ts --compile --outfile slangroom-exec +slangroom-exec: $(SOURCES) $(LIBS) ## ๐Ÿ› ๏ธ Build slangroom-exec + bun build ./src/index.ts --compile --outfile slangroom-exec clean: ## ๐Ÿงน Clean the build @rm -f slangroom-exec @echo "๐Ÿงน Cleaned the build" tests: slangroom-exec ## ๐Ÿงช Run tests - ./test/bats/bin/bats test/test.bats + ./test/bats/bin/bats -j 15 test/*.bats + bun test --coverage + +$(LIBS): package.json + bun i + +video: + PATH=docs:$$PATH + cd docs && vhs slangroom-exec.tape diff --git a/README.md b/README.md deleted file mode 100644 index 1e82858..0000000 --- a/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# slangroom-exec - -To install dependencies: - -```bash -bun install -``` - -To run: - -```bash -bun run index.ts -``` - -This project was created using `bun init` in bun v1.1.7. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/bun.lockb b/bun.lockb index 680d5bb..cb054a7 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..e5133e9 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[test] +coverageThreshold = 0.9 diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 0000000..5a3d1bd --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,6 @@ +Generate the video with https://github.com/charmbracelet/vhs +by running + +```bash +vhs slangroom-exec.tape +``` diff --git a/docs/simple_slangroom.slang b/docs/simple_slangroom.slang new file mode 120000 index 0000000..8f10708 --- /dev/null +++ b/docs/simple_slangroom.slang @@ -0,0 +1 @@ +../test/fixtures/simple_slangroom.slang \ No newline at end of file diff --git a/docs/slangroom-exec.gif b/docs/slangroom-exec.gif new file mode 100644 index 0000000..9673671 Binary files /dev/null and b/docs/slangroom-exec.gif differ diff --git a/docs/slangroom-exec.tape b/docs/slangroom-exec.tape new file mode 100644 index 0000000..8845253 --- /dev/null +++ b/docs/slangroom-exec.tape @@ -0,0 +1,17 @@ +Output slangroom-exec.gif + +Set Shell fish +Type "cat simple_slangroom.slang" +Sleep 500ms +Enter +Sleep 10.5s +Escape +Type "cat simple_slangroom.slang | slexfe" +Sleep 2s +Enter +Sleep 6.5s +Type "cat simple_slangroom.slang | slexfe | slangroom-exec" +Sleep 1.5s +Enter +Sleep 3.5s + diff --git a/docs/slexfe b/docs/slexfe new file mode 120000 index 0000000..8fc0cb9 --- /dev/null +++ b/docs/slexfe @@ -0,0 +1 @@ +../src/slexfe \ No newline at end of file diff --git a/index.ts b/index.ts deleted file mode 100644 index cf57a22..0000000 --- a/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Slangroom } from "@slangroom/core"; -import { ethereum } from "@slangroom/ethereum"; -import { fs } from "@slangroom/fs"; -import { git } from "@slangroom/git"; -import { helpers } from "@slangroom/helpers"; -import { http } from "@slangroom/http"; -import { JSONSchema } from "@slangroom/json-schema"; -import { oauth } from "@slangroom/oauth"; -import { pocketbase } from "@slangroom/pocketbase"; -import { qrcode } from "@slangroom/qrcode"; -import { redis } from "@slangroom/redis"; -import type { ZenParams } from "@slangroom/shared"; -import { shell } from "@slangroom/shell"; -import { timestamp } from "@slangroom/timestamp"; -import { wallet } from "@slangroom/wallet"; -import { zencode } from "@slangroom/zencode"; - -const the_input = await Bun.stdin.text(); -const s = new Slangroom([ - ethereum, - fs, - git, - helpers, - http, - JSONSchema, - oauth, - pocketbase, - qrcode, - redis, - shell, - timestamp, - wallet, - zencode, -]); - -const decode_and_trim = (r: string) => - Buffer.from(r, "base64").toString().trim(); -const decode = (r: string) => Buffer.from(r, "base64").toString(); -const decode_and_json = (r: string) => JSON.parse(decode(r)); -type Names = "conf" | "data" | "keys" | "extra"; - -const decode_param = (source: string, name: Names, fn: Function) => { - if (source && source !== "") { - try { - opts[name] = fn(source); - } catch (e) { - console.error(`${name} is malformed`); - process.exit(2); - } - } -}; - -const [c, sl, d, k, e, cx] = the_input.split("\n"); -const opts: ZenParams = { data: {}, keys: {} }; -const contract = decode(sl); -decode_param(c, "conf", decode_and_trim); -decode_param(d, "data", decode_and_json); -decode_param(k, "keys", decode_and_json); -decode_param(e, "extra", decode_and_json); -const { result } = await s.execute(contract, opts); -await Bun.write(Bun.stdout, JSON.stringify(result)); diff --git a/license b/license new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/license @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/package.json b/package.json index f76423a..a5a2bc8 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@slangroom/shell": "^1.30.0", "@slangroom/timestamp": "^1.30.0", "@slangroom/wallet": "^1.30.0", - "@slangroom/zencode": "^1.30.0" + "@slangroom/zencode": "^1.30.0", + "type-fest": "^4.18.2" } } \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e52dc85 --- /dev/null +++ b/readme.md @@ -0,0 +1,65 @@ +# slangroom-exec + +The missing slangroom executor. We are working the .wasm transpile of the +slangroom, but in the meantime this repo could be used similar to the `zencode-exec` +to embed [https://dyne.org/slangroom](slangroom) into other languages. + +`slangroom-exec` is a simple utility that reads from STDIN the following content + +1. conf +1. slangroom-contract +1. data +1. keys +1. extra +1. context + +separated each per new-line and encoded in `base64` and outputs the slangroom execution to stoud. + +### Demo + +![Slangroom-exec Demo](./docs/slangroom-exec.gif) + +## SLangroom-EXec Format Encoder + +This script is used to encode the format of the slangroom-exec command into a string that can be used in the slangroom-exec command. + +The script accepts the six parameters that are used in the slangroom-exec command and encodes them into a string. The encoded string is then printed to stdout. + +### Usage + +For each of the parameters, the script also has option flags: + +-c or --conf for conf +-s or --slangroom-contract for slangroom-contract +-d or --data for data +-k or --keys for keys +-e or --extra for extra +-x or --context for context +-F or --filename lookup files based on a prefix +-h or --help to print the help message + +#### The named convention `-F` option flag + +When you have a suite of files if you follow the formal slangroom name convention as such: + +conf: `${prefix}.conf` +slangroom-contract: `${prefix}.slang` +data: `${prefix}.data.json` +keys: `${prefix}.keys.json` +extra: `${prefix}.extra.json` +context: `${prefix}.context` + +you can just run + +#### STDIN + +if you just pass something in `/dev/stdin` is interpreted as the contract. +This also overwrites the `--slangroom-contract` option flag if passed as a duplicate. + +## Examples + +To encode a slangroom-contract and data, you can run: + +To encode parameters from a file, you can run: + +Where myParameters.txt is a file containing your parameters. diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..14f531e --- /dev/null +++ b/src/index.ts @@ -0,0 +1,5 @@ +import { slangroom_exec } from "./lib"; + +const the_input = await Bun.stdin.text(); +const the_output = await slangroom_exec(the_input); +await Bun.write(Bun.stdout, the_output); diff --git a/src/lib.ts b/src/lib.ts new file mode 100644 index 0000000..491bbbd --- /dev/null +++ b/src/lib.ts @@ -0,0 +1,101 @@ +import { Slangroom } from "@slangroom/core"; +import { ethereum } from "@slangroom/ethereum"; +import { fs } from "@slangroom/fs"; +import { git } from "@slangroom/git"; +import { helpers } from "@slangroom/helpers"; +import { http } from "@slangroom/http"; +import { JSONSchema } from "@slangroom/json-schema"; +import { oauth } from "@slangroom/oauth"; +import { pocketbase } from "@slangroom/pocketbase"; +import { qrcode } from "@slangroom/qrcode"; +import { redis } from "@slangroom/redis"; +import { shell } from "@slangroom/shell"; +import { timestamp } from "@slangroom/timestamp"; +import { wallet } from "@slangroom/wallet"; +import { zencode } from "@slangroom/zencode"; + +import type { ZenParams } from "@slangroom/shared"; +import type { StringKeyOf } from "type-fest"; +type ZenParamKey = StringKeyOf; + +export const decode = (r: string) => Buffer.from(r, "base64").toString(); +export const decode_and_trim = (r: string) => decode(r).trim(); +export const decode_and_json = (r: string) => { + try { + return JSON.parse(decode(r)); + } catch (e) { + console.error("JSON is malformed"); + process.exit(3); + } +}; + +export const slangroom_exec = async (input: string) => { + const s = new Slangroom([ + ethereum, + fs, + git, + helpers, + http, + JSONSchema, + oauth, + pocketbase, + qrcode, + redis, + shell, + timestamp, + wallet, + zencode, + ]); + + const [c, sl, d, k, e, cx] = input.split("\n"); + + if (sl.trim().length === 0) { + console.error("Slangroom contract is empty"); + process.exit(1); + } + + const opts: ZenParams = { data: {}, keys: {} }; + const contract = decode(sl); + + const decode_slangroom_param = ( + source: string, + key: ZenParamKey, + fn: Function + ) => { + if (source && source !== "") { + try { + opts[key] = fn(source); + } catch (e) { + console.error(`${key.toUpperCase()} is malformed`); + process.exit(2); + } + } + }; + + decode_slangroom_param(c, "conf", decode_and_trim); + decode_slangroom_param(d, "data", decode_and_json); + decode_slangroom_param(k, "keys", decode_and_json); + decode_slangroom_param(e, "extra", decode_and_json); + const { result } = await s.execute(contract, opts); + return JSON.stringify(result); +}; + +export const encode = ( + conf?: string, + contract?: string, + data?: string, + keys?: string, + extra?: string, + ctx?: string +) => { + const b64 = (source: string) => Buffer.from(source).toString("base64"); + + return [ + b64(conf ?? ""), + b64(contract ?? ""), + b64(data ?? ""), + b64(keys ?? ""), + b64(extra ?? ""), + b64(ctx ?? ""), + ].join("\n"); +}; diff --git a/src/slexfe b/src/slexfe new file mode 100755 index 0000000..d2d994f --- /dev/null +++ b/src/slexfe @@ -0,0 +1,95 @@ +#!/bin/env bash +# SLangroom-EXec Format Encoder +# This script is used to encode the format of the slangroom-exec command +# into a string that can be used in the slangroom-exec command + +print_help() { + echo -e "\033[1mUsage:\033[0m" + echo -e " $0 [options]\n" + echo -e "\033[1mOptions:\033[0m" + echo -e " -c, --conf conf filename to read" + echo -e " -s, --slangroom-contract slangroom-contract filename to read" + echo -e " -d, --data data filename to read" + echo -e " -k, --keys keys filename to read" + echo -e " -e, --extra extra filename to read" + echo -e " -x, --context context filename to read" + echo -e " -F, --filename lookup files based on a prefix" + echo -e " -h, --help Print this help message" + echo -e "\nEncode the parameters into a base64 string." + exit 1 +} + +# Function to encode to base64 +encode_base64() { + cat "$1" 2>/dev/null | base64 -w 0 +} + +encode_json_base64() { + jq -c . "$1" 2>/dev/null | base64 -w 0 +} + +# check that jq and getopt are installed + +if ! command -v jq &> /dev/null; then + echo "jq is not installed. Please install jq to use this script." + exit 1 +fi + +if ! command -v getopt &> /dev/null; then + echo "getopt is not installed. Please install getopt to use this script." + exit 1 +fi + +# Default values for options +conf="" +slangroom_contract="" +data="" +keys="" +extra="" +context="" + +OPTIONS=$(getopt -o c:s:d:k:e:x:F:h --long conf:,slangroom-contract:,data:,keys:,extra:,context:,filename:,help -n 'parse-options' -- "$@") +if [ $? -ne 0 ]; then + echo "Failed to parse options." >&2 + exit 1 +fi +eval set -- "$OPTIONS" + +while true; do + case "$1" in + -c|--conf) + conf=$(encode_base64 "$2"); shift 2 ;; + -s|--slangroom-contract) + slangroom_contract=$(encode_base64 "$2"); shift 2 ;; + -d|--data) + data=$(encode_json_base64 "$2"); shift 2 ;; + -k|--keys) + keys=$(encode_json_base64 "$2"); shift 2 ;; + -e|--extra) + extra=$(encode_json_base64 "$2"); shift 2 ;; + -x|--context) + context=$(encode_base64 "$2"); shift 2 ;; + -F|--filename) + conf=$(encode_base64 "${2}.conf") + slangroom_contract=$(encode_base64 "${2}.slang") + data=$(encode_json_base64 "${2}.data.json") + keys=$(encode_json_base64 "${2}.keys.json") + extra=$(encode_json_base64 "${2}.extra.json") + context=$(encode_base64 "${2}.context") + shift 2 ;; + -h|--help) + print_help ;; + --) + shift; break ;; + *) + echo "Internal error!"; exit 1 ;; + esac +done + +# if slangroom-contract is empty, read it from stdin +if [ -z "$slangroom_contract" ]; then + slangroom_contract=$(encode_base64 -) +fi + +# Print the encoded string +echo -e "${conf}\n${slangroom_contract}\n${data}\n${keys}\n${extra}\n${context}" \ No newline at end of file diff --git a/test/test.bats b/test/e2e.bats similarity index 71% rename from test/test.bats rename to test/e2e.bats index 259601d..8a1bef2 100644 --- a/test/test.bats +++ b/test/e2e.bats @@ -34,4 +34,18 @@ setup() { run_slangroom_exec assert_output --partial "Do you know who greets you? ๐Ÿฅ’" assert_success +} + +@test "should fail on empty slangroom" { + load_fixture "broken_conf" + run_slangroom_exec + assert_output "Slangroom contract is empty" + assert_failure 1 +} + +@test "should fail on broken slangroom" { + load_fixture "broken_slangroom" + run_slangroom_exec + assert_output --partial "Invalid Zencode prefix 1: 'Gibberish'" + assert_failure 1 } \ No newline at end of file diff --git a/test/fixtures/broken_conf.conf b/test/fixtures/broken_conf.conf new file mode 100644 index 0000000..d68664b --- /dev/null +++ b/test/fixtures/broken_conf.conf @@ -0,0 +1,6 @@ +aosijoaij + + + + + diff --git a/test/fixtures/broken_slangroom.slang b/test/fixtures/broken_slangroom.slang new file mode 100644 index 0000000..c803eb1 --- /dev/null +++ b/test/fixtures/broken_slangroom.slang @@ -0,0 +1 @@ +Gibberish \ No newline at end of file diff --git a/test/fixtures/simple_slangroom.slang b/test/fixtures/simple_slangroom.slang index e9656f2..5f84eed 100644 --- a/test/fixtures/simple_slangroom.slang +++ b/test/fixtures/simple_slangroom.slang @@ -1,4 +1,4 @@ Rule unknown ignore Given I fetch the local timestamp in seconds and output into 'timestamp' Given I have a 'number' named 'timestamp' -Then print the 'timestamp' +Then print the 'timestamp' \ No newline at end of file diff --git a/test/test_helper/common-setup.bash b/test/test_helper/common-setup.bash index 85acca2..aef6f5c 100644 --- a/test/test_helper/common-setup.bash +++ b/test/test_helper/common-setup.bash @@ -14,46 +14,12 @@ _common_setup() { } load_fixture() { - name=$1 - - conf_file="test/fixtures/${name}.conf" - zencode_file="test/fixtures/${name}.slang" - keys_file="test/fixtures/${name}.keys.json" - data_file="test/fixtures/${name}.data.json" - extra_file="test/fixtures/${name}.extra.json" - context_file="test/fixtures/${name}.context.json" - - conf="" - zencode="" - keys="" - data="" - extra="" - context="" - - if [ -f "$conf_file" ]; then - conf=$(cat "$conf_file") - fi - if [ -f "$zencode_file" ]; then - zencode=$(base64 -w 0 "$zencode_file") - fi - if [ -f "$keys_file" ]; then - keys=$(jq -c . "$keys_file" | base64 -w 0) - fi - if [ -f "$data_file" ]; then - data=$(jq -c . "$data_file" | base64 -w 0) - fi - if [ -f "$extra_file" ]; then - extra=$(jq -c . "$extra_file" | base64 -w 0) - fi - if [ -f "$context_file" ]; then - context=$(jq -c . "$context_file" | base64 -w 0) - fi - - printf -v slang_input '%s\n%s\n%s\n%s\n%s\n%s' "$conf" "$zencode" "$keys" "$data" "$extra" "$context" + encoded=$(src/slexfe -F test/fixtures/$1) + printf -v slang_input '%s' "$encoded" } run_slangroom_exec() { bats_require_minimum_version 1.5.0 - run -0 bats_pipe echo "$slang_input" \| ./slangroom-exec + run bats_pipe echo "$slang_input" \| ./slangroom-exec } \ No newline at end of file diff --git a/test/unit.test.ts b/test/unit.test.ts new file mode 100644 index 0000000..d792c1b --- /dev/null +++ b/test/unit.test.ts @@ -0,0 +1,14 @@ +import { expect, test } from "bun:test"; +import { encode } from "../src/lib"; + +test("the encode() utility should work ok", () => { + const conf = ""; + const slang = `Rule unknown ignore +Given I fetch the local timestamp in seconds and output into 'timestamp' +Given I have a 'number' named 'timestamp' +Then print the 'timestamp'`; + const have = encode(conf, slang, "", "", ""); + const want = + "\nUnVsZSB1bmtub3duIGlnbm9yZQpHaXZlbiBJIGZldGNoIHRoZSBsb2NhbCB0aW1lc3RhbXAgaW4gc2Vjb25kcyBhbmQgb3V0cHV0IGludG8gJ3RpbWVzdGFtcCcKR2l2ZW4gSSBoYXZlIGEgJ251bWJlcicgbmFtZWQgJ3RpbWVzdGFtcCcKVGhlbiBwcmludCB0aGUgJ3RpbWVzdGFtcCc=\n\n\n\n"; + expect(have).toBe(want); +});