diff --git a/docs/typescript-plutus/api/lbf/build.nix b/docs/typescript-plutus/api/lbf/build.nix index 6665ae23..8edbbba5 100644 --- a/docs/typescript-plutus/api/lbf/build.nix +++ b/docs/typescript-plutus/api/lbf/build.nix @@ -2,7 +2,7 @@ _: { perSystem = { config, ... }: let - tsFlake = + lbf-plutus-sample-project-typescript = config.lbf-nix.lbfPlutusTypescript { name = "lbf-plutus-sample-project"; src = ./.; @@ -11,7 +11,7 @@ _: in { packages = { - inherit (tsFlake.packages) lbf-plutus-sample-project-typescript lbf-plutus-sample-project-typescript-tgz; + inherit lbf-plutus-sample-project-typescript; }; }; } diff --git a/docs/typescript-plutus/build.nix b/docs/typescript-plutus/build.nix index d3c449de..d2e78696 100644 --- a/docs/typescript-plutus/build.nix +++ b/docs/typescript-plutus/build.nix @@ -10,7 +10,7 @@ src = ./.; npmExtraDependencies = [ - config.packages.lbf-plutus-sample-project-typescript-tgz + config.packages.lbf-plutus-sample-project-typescript ]; devShellTools = config.settings.shell.tools; diff --git a/docs/typescript-prelude/api/lbf/build.nix b/docs/typescript-prelude/api/lbf/build.nix index d4b983bb..82638851 100644 --- a/docs/typescript-prelude/api/lbf/build.nix +++ b/docs/typescript-prelude/api/lbf/build.nix @@ -2,7 +2,7 @@ _: { perSystem = { config, ... }: let - tsFlake = + lbf-prelude-sample-project-typescript = config.lbf-nix.lbfPreludeTypescript { name = "lbf-prelude-sample-project"; src = ./.; @@ -11,7 +11,7 @@ _: in { packages = { - inherit (tsFlake.packages) lbf-prelude-sample-project-typescript lbf-prelude-sample-project-typescript-tgz; + inherit lbf-prelude-sample-project-typescript; }; }; } diff --git a/docs/typescript-prelude/build.nix b/docs/typescript-prelude/build.nix index e83f1078..c7547504 100644 --- a/docs/typescript-prelude/build.nix +++ b/docs/typescript-prelude/build.nix @@ -9,7 +9,7 @@ name = "prelude-sample-project"; src = ./.; npmExtraDependencies = [ - config.packages.lbf-prelude-sample-project-typescript-tgz + config.packages.lbf-prelude-sample-project-typescript ]; devShellTools = config.settings.shell.tools; diff --git a/extras/lbf-nix/lbf-typescript.nix b/extras/lbf-nix/lbf-typescript.nix index 9efd4420..7367b516 100644 --- a/extras/lbf-nix/lbf-typescript.nix +++ b/extras/lbf-nix/lbf-typescript.nix @@ -276,4 +276,4 @@ let }); }); in -lbTypescriptFlake +lbTypescriptFlake.packages."${name}-typescript-tgz" diff --git a/flake.nix b/flake.nix index 2b970e4e..3af3c325 100644 --- a/flake.nix +++ b/flake.nix @@ -88,10 +88,12 @@ ./testsuites/lbt-prelude/lbt-prelude-haskell/build.nix ./testsuites/lbt-prelude/lbt-prelude-purescript/build.nix ./testsuites/lbt-prelude/lbt-prelude-rust/build.nix + ./testsuites/lbt-prelude/lbt-prelude-typescript/build.nix ./testsuites/lbt-plutus/api/build.nix ./testsuites/lbt-plutus/golden/build.nix ./testsuites/lbt-plutus/lbt-plutus-haskell/build.nix ./testsuites/lbt-plutus/lbt-plutus-purescript/build.nix + ./testsuites/lbt-plutus/lbt-plutus-typescript/build.nix ./testsuites/lbt-plutus/lbt-plutus-plutarch/build.nix ./testsuites/lbt-plutus/lbt-plutus-rust/build.nix ./experimental/build.nix diff --git a/lambda-buffers-codegen/src/LambdaBuffers/Codegen/Typescript/Print/LamVal.hs b/lambda-buffers-codegen/src/LambdaBuffers/Codegen/Typescript/Print/LamVal.hs index 14223f19..6edb690c 100644 --- a/lambda-buffers-codegen/src/LambdaBuffers/Codegen/Typescript/Print/LamVal.hs +++ b/lambda-buffers-codegen/src/LambdaBuffers/Codegen/Typescript/Print/LamVal.hs @@ -450,8 +450,8 @@ printCtorE ((_, tyN), (ctorN, _)) prodVals = do [ lbrace , indent 2 $ vsep - [ "name" <+> colon <+> pretty '\'' <> ctorNDoc <> pretty '\'' <> comma - , "fields" <+> colon <+> singleDoc + [ "fields" <+> colon <+> singleDoc <> comma + , "name" <+> colon <+> pretty '\'' <> ctorNDoc <> pretty '\'' ] , rbrace ] @@ -461,8 +461,8 @@ printCtorE ((_, tyN), (ctorN, _)) prodVals = do [ lbrace , indent 2 $ vsep - [ "name" <+> colon <+> pretty '\'' <> ctorNDoc <> pretty '\'' <> comma - , "fields" <+> colon <+> encloseSep lbracket rbracket comma prodDocs + [ "fields" <+> colon <+> encloseSep lbracket rbracket comma prodDocs <> comma + , "name" <+> colon <+> pretty '\'' <> ctorNDoc <> pretty '\'' ] , rbrace ] diff --git a/libs/build.nix b/libs/build.nix index 3b96f102..2e9c47ac 100644 --- a/libs/build.nix +++ b/libs/build.nix @@ -28,7 +28,7 @@ configs = [ "${config.packages.codegen-configs}/purescript-prelude-base.json" ]; }; - lbf-prelude-typescript = (config.lbf-nix.lbfTypescript { + lbf-prelude-typescript = config.lbf-nix.lbfTypescript { name = "lbf-prelude"; src = ./lbf-prelude; files = [ "Prelude.lbf" ]; @@ -38,7 +38,7 @@ [ config.packages.lbr-prelude-typescript-tgz ]; - }).packages.lbf-prelude-typescript-tgz; + }; lbf-prelude-plutarch = config.lbf-nix.lbfPlutarch' { name = "lbf-prelude-plutarch"; @@ -108,7 +108,7 @@ ]; }; - lbf-plutus-typescript = (config.lbf-nix.lbfTypescript { + lbf-plutus-typescript = config.lbf-nix.lbfTypescript { name = "lbf-plutus"; src = ./lbf-plutus; files = [ "Plutus/V1.lbf" "Plutus/V2.lbf" ]; @@ -123,7 +123,7 @@ config.packages.lbf-prelude-typescript config.packages.lbr-plutus-typescript-tgz ]; - }).packages.lbf-plutus-typescript-tgz; + }; lbf-plutus-rust = config.lbf-nix.lbfRust { name = "lbf-plutus"; diff --git a/runtimes/typescript/lbr-plutus/package-lock.json b/runtimes/typescript/lbr-plutus/package-lock.json index 36e043f0..f1f303eb 100644 --- a/runtimes/typescript/lbr-plutus/package-lock.json +++ b/runtimes/typescript/lbr-plutus/package-lock.json @@ -53,7 +53,7 @@ "node_modules/lbr-prelude": { "version": "1.0.0", "resolved": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", - "integrity": "sha512-dCBgP+JBfVGXGUXpmar1fsJ5zrgyMlShGdiD5MAMgdRYe4CcgE0CINS+Xq7vHgTFmWXib3DQ/QXIJCvoM91cvg==", + "integrity": "sha512-xakEfmSqbcYPCFeNFPAHdm2japkfw/a2Hh/kHeA+VZmEaKyVP8JrWJvtNWjfn/k7Dkbj0lbBl48LQWMFkqvjcQ==", "license": "ISC", "dependencies": { "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz", @@ -63,7 +63,7 @@ "node_modules/plutus-ledger-api": { "version": "1.0.0", "resolved": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", - "integrity": "sha512-CR4CshLrbu15tUu7RPA3fvzRZ/HjOsBOJJ4P144b3dtzToP2ljJ4KbCZs3pk3im71sst7fjQKdEM97B3fyMc3Q==", + "integrity": "sha512-UVQfoeULxTnLszKH6u1yYWiYXjqn6LulX02awhi0bX8Bvnoj0hF43EH3e+RrcMDe/6bQh+TVw7N2Xu+RU14esw==", "license": "ISC", "dependencies": { "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" @@ -72,7 +72,7 @@ "node_modules/prelude": { "version": "1.0.1", "resolved": "file:.extra-dependencies/prelude-1.0.1.tgz", - "integrity": "sha512-ucWNcpO/mDC3kgAEx/SP9yzmhWH5FgP8TY1kQpPQw8R0zGHtgpdXnmcKcl1n8s92uIDgBtIZGVUNRoEDLjGwqQ==", + "integrity": "sha512-NtSJIsn0+eDmzHZu3o5rK5Fh2Q3/eXmZ+tcsUsip7XmKl06RX0vTWWSsYngJhsdj+Xd7jZl/Wy92m8oNycykVA==", "license": "ISC" }, "node_modules/pure-rand": { @@ -131,7 +131,7 @@ }, "lbr-prelude": { "version": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", - "integrity": "sha512-dCBgP+JBfVGXGUXpmar1fsJ5zrgyMlShGdiD5MAMgdRYe4CcgE0CINS+Xq7vHgTFmWXib3DQ/QXIJCvoM91cvg==", + "integrity": "sha512-xakEfmSqbcYPCFeNFPAHdm2japkfw/a2Hh/kHeA+VZmEaKyVP8JrWJvtNWjfn/k7Dkbj0lbBl48LQWMFkqvjcQ==", "requires": { "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz", "typescript": "^5.3.3" @@ -139,14 +139,14 @@ }, "plutus-ledger-api": { "version": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", - "integrity": "sha512-CR4CshLrbu15tUu7RPA3fvzRZ/HjOsBOJJ4P144b3dtzToP2ljJ4KbCZs3pk3im71sst7fjQKdEM97B3fyMc3Q==", + "integrity": "sha512-UVQfoeULxTnLszKH6u1yYWiYXjqn6LulX02awhi0bX8Bvnoj0hF43EH3e+RrcMDe/6bQh+TVw7N2Xu+RU14esw==", "requires": { "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" } }, "prelude": { "version": "file:.extra-dependencies/prelude-1.0.1.tgz", - "integrity": "sha512-ucWNcpO/mDC3kgAEx/SP9yzmhWH5FgP8TY1kQpPQw8R0zGHtgpdXnmcKcl1n8s92uIDgBtIZGVUNRoEDLjGwqQ==" + "integrity": "sha512-NtSJIsn0+eDmzHZu3o5rK5Fh2Q3/eXmZ+tcsUsip7XmKl06RX0vTWWSsYngJhsdj+Xd7jZl/Wy92m8oNycykVA==" }, "pure-rand": { "version": "6.0.4", diff --git a/runtimes/typescript/lbr-plutus/src/LambdaBuffers/PlutusData.ts b/runtimes/typescript/lbr-plutus/src/LambdaBuffers/PlutusData.ts index da8f648e..40a39d49 100644 --- a/runtimes/typescript/lbr-plutus/src/LambdaBuffers/PlutusData.ts +++ b/runtimes/typescript/lbr-plutus/src/LambdaBuffers/PlutusData.ts @@ -54,3 +54,5 @@ export interface IsPlutusDataInstances { dictA: PlutusLedgerApiPlutusData.IsPlutusData, ) => PlutusLedgerApiPlutusData.IsPlutusData>; } +IsPlutusData[LbrPrelude.List] = + PlutusLedgerApiPreludeInstances.isPlutusDataList; diff --git a/runtimes/typescript/lbr-plutus/src/LambdaBuffers/V1/Instances.ts b/runtimes/typescript/lbr-plutus/src/LambdaBuffers/V1/Instances.ts index f83da642..f84d8eea 100644 --- a/runtimes/typescript/lbr-plutus/src/LambdaBuffers/V1/Instances.ts +++ b/runtimes/typescript/lbr-plutus/src/LambdaBuffers/V1/Instances.ts @@ -1,5 +1,6 @@ import * as PlutusData from "../PlutusData.js"; import * as PlutusLedgerApiPlutusData from "plutus-ledger-api/PlutusData.js"; +import * as PlutusLedgerApiAssocMap from "plutus-ledger-api/AssocMap.js"; import * as PlutusLedgerApiV1 from "plutus-ledger-api/V1.js"; import * as LbrPrelude from "lbr-prelude"; import * as Prelude from "prelude"; @@ -247,6 +248,29 @@ declare module "../PlutusData.js" { PlutusData.IsPlutusData[Symbols.UpperBound] = PlutusLedgerApiV1.isPlutusDataUpperBound; +// Closure +declare module "lbr-prelude" { + export interface EqInstances { + [Symbols.Closure]: Prelude.Eq; + } + + export interface JsonInstances { + [Symbols.Closure]: Prelude.Json; + } +} + +LbrPrelude.Eq[Symbols.Closure] = Prelude.eqBool; +LbrPrelude.Json[Symbols.Closure] = Prelude.jsonBool; + +declare module "../PlutusData.js" { + export interface IsPlutusDataInstances { + [Symbols.Closure]: PlutusLedgerApiPlutusData.IsPlutusData< + PlutusLedgerApiV1.Closure + >; + } +} +PlutusData.IsPlutusData[Symbols.Closure] = PlutusLedgerApiV1.isPlutusDataBool; + // Redeemer declare module "lbr-prelude" { export interface EqInstances { @@ -579,3 +603,35 @@ declare module "../PlutusData.js" { } PlutusData.IsPlutusData[Symbols.PlutusData] = PlutusLedgerApiPlutusData.isPlutusDataPlutusData; + +// Map +declare module "lbr-prelude" { + export interface EqInstances { + [Symbols.Map]: ( + dictK: Prelude.Eq, + dictV: Prelude.Eq, + ) => Prelude.Eq>; + } + + export interface JsonInstances { + [Symbols.Map]: ( + dictK: Prelude.Json, + dictV: Prelude.Json, + ) => Prelude.Json>; + } +} + +LbrPrelude.Eq[Symbols.Map] = PlutusLedgerApiAssocMap.eqMap; +LbrPrelude.Json[Symbols.Map] = PlutusLedgerApiAssocMap.jsonMap; + +declare module "../PlutusData.js" { + export interface IsPlutusDataInstances { + [Symbols.Map]: ( + dictK: PlutusLedgerApiPlutusData.IsPlutusData, + dictV: PlutusLedgerApiPlutusData.IsPlutusData, + ) => PlutusLedgerApiPlutusData.IsPlutusData< + PlutusLedgerApiAssocMap.Map + >; + } +} +PlutusData.IsPlutusData[Symbols.Map] = PlutusLedgerApiAssocMap.isPlutusDataMap; diff --git a/runtimes/typescript/lbr-plutus/src/LambdaBuffers/V1/Symbols.ts b/runtimes/typescript/lbr-plutus/src/LambdaBuffers/V1/Symbols.ts index fa16d92c..58726f2f 100644 --- a/runtimes/typescript/lbr-plutus/src/LambdaBuffers/V1/Symbols.ts +++ b/runtimes/typescript/lbr-plutus/src/LambdaBuffers/V1/Symbols.ts @@ -11,6 +11,7 @@ export const Interval: unique symbol = Symbol("Interval"); export const Extended: unique symbol = Symbol("Extended"); export const LowerBound: unique symbol = Symbol("LowerBound"); export const UpperBound: unique symbol = Symbol("UpperBound"); +export const Closure: unique symbol = Symbol("Closure"); export const Redeemer: unique symbol = Symbol("Redeemer"); export const Datum: unique symbol = Symbol("Datum"); export const DatumHash: unique symbol = Symbol("DatumHash"); @@ -36,6 +37,7 @@ export type Interval = PlutusLedgerApiV1.Interval; export type Extended = PlutusLedgerApiV1.Extended; export type LowerBound = PlutusLedgerApiV1.LowerBound; export type UpperBound = PlutusLedgerApiV1.UpperBound; +export type Closure = PlutusLedgerApiV1.Closure; export type Redeemer = PlutusLedgerApiV1.Redeemer; export type Datum = PlutusLedgerApiV1.Datum; export type DatumHash = PlutusLedgerApiV1.DatumHash; diff --git a/runtimes/typescript/lbr-prelude/package-lock.json b/runtimes/typescript/lbr-prelude/package-lock.json index 6a4e9e65..e527d978 100644 --- a/runtimes/typescript/lbr-prelude/package-lock.json +++ b/runtimes/typescript/lbr-prelude/package-lock.json @@ -16,7 +16,7 @@ "node_modules/prelude": { "version": "1.0.1", "resolved": "file:.extra-dependencies/prelude-1.0.1.tgz", - "integrity": "sha512-ucWNcpO/mDC3kgAEx/SP9yzmhWH5FgP8TY1kQpPQw8R0zGHtgpdXnmcKcl1n8s92uIDgBtIZGVUNRoEDLjGwqQ==", + "integrity": "sha512-NtSJIsn0+eDmzHZu3o5rK5Fh2Q3/eXmZ+tcsUsip7XmKl06RX0vTWWSsYngJhsdj+Xd7jZl/Wy92m8oNycykVA==", "license": "ISC" }, "node_modules/typescript": { @@ -35,7 +35,7 @@ "dependencies": { "prelude": { "version": "file:.extra-dependencies/prelude-1.0.1.tgz", - "integrity": "sha512-ucWNcpO/mDC3kgAEx/SP9yzmhWH5FgP8TY1kQpPQw8R0zGHtgpdXnmcKcl1n8s92uIDgBtIZGVUNRoEDLjGwqQ==" + "integrity": "sha512-NtSJIsn0+eDmzHZu3o5rK5Fh2Q3/eXmZ+tcsUsip7XmKl06RX0vTWWSsYngJhsdj+Xd7jZl/Wy92m8oNycykVA==" }, "typescript": { "version": "5.3.3", diff --git a/testsuites/lbt-plutus/api/build.nix b/testsuites/lbt-plutus/api/build.nix index 6409a845..35242f41 100644 --- a/testsuites/lbt-plutus/api/build.nix +++ b/testsuites/lbt-plutus/api/build.nix @@ -26,6 +26,12 @@ _: { files = [ "Foo.lbf" "Foo/Bar.lbf" "Days.lbf" ]; }; + lbf-plutus-golden-api-typescript = config.lbf-nix.lbfPlutusTypescript { + name = "lbf-plutus-golden-api"; + src = ./.; + files = [ "Foo.lbf" "Foo/Bar.lbf" "Days.lbf" ]; + }; + }; }; } diff --git a/testsuites/lbt-plutus/golden/build.nix b/testsuites/lbt-plutus/golden/build.nix index cc985ea3..100410ba 100644 --- a/testsuites/lbt-plutus/golden/build.nix +++ b/testsuites/lbt-plutus/golden/build.nix @@ -1,6 +1,17 @@ _: { perSystem = { pkgs, config, ... }: + let + goldenData = pkgs.stdenv.mkDerivation { + name = "lbt-plutus-golden-data"; + src = ./.; + # Disable the Fixup phase since it needs to (potentially) write to the + # files in `./.` which are readonly in the nix store + dontFixup = true; + installPhase = ''ln -s "$src" "$out"''; + }; + + in { packages = { lbt-plutus-golden-haskell = config.lbf-nix.haskellData { @@ -9,19 +20,11 @@ _: cabalPackageName = "lbt-plutus-golden-data"; }; - lbt-plutus-golden-purescript = pkgs.stdenv.mkDerivation { - name = "lbt-plutus-golden-data"; - src = ./.; - phases = "installPhase"; - installPhase = "ln -s $src $out"; - }; + lbt-plutus-golden-purescript = goldenData; - lbt-plutus-golden-rust = pkgs.stdenv.mkDerivation { - name = "lbt-plutus-golden-data"; - src = ./.; - phases = "installPhase"; - installPhase = "ln -s $src $out"; - }; + lbt-plutus-golden-rust = goldenData; + + lbt-plutus-golden-typescript = goldenData; }; }; diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/.gitignore b/testsuites/lbt-plutus/lbt-plutus-typescript/.gitignore new file mode 100644 index 00000000..c8d23906 --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/.gitignore @@ -0,0 +1,4 @@ +# Ignore the autogenerated files from flake-lang.nix +.extra-dependencies +data +node_modules diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/build.nix b/testsuites/lbt-plutus/lbt-plutus-typescript/build.nix new file mode 100644 index 00000000..45ab1aa1 --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/build.nix @@ -0,0 +1,28 @@ +{ inputs, ... }: +{ + perSystem = { config, system, ... }: + let + tsFlake = + inputs.flake-lang.lib.${system}.typescriptFlake { + name = "lbt-plutus"; + src = ./.; + npmExtraDependencies = [ + config.packages.lbf-plutus-golden-api-typescript + ]; + + devShellTools = config.settings.shell.tools; + devShellHook = config.settings.shell.hook; + + data = + [ + { + name = "lbt-plutus-golden-data"; + path = config.packages.lbt-plutus-golden-typescript; + } + ]; + }; + in + { + inherit (tsFlake) devShells checks; + }; +} diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/package-lock.json b/testsuites/lbt-plutus/lbt-plutus-typescript/package-lock.json new file mode 100644 index 00000000..93fd7d48 --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/package-lock.json @@ -0,0 +1,204 @@ +{ + "name": "lbf-prelude-typescript", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "lbf-prelude-typescript", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "lbf-plutus": "file:.extra-dependencies/lbf-plutus-1.0.0.tgz", + "lbf-plutus-golden-api": "file:.extra-dependencies/lbf-plutus-golden-api-1.0.0.tgz", + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbr-plutus": "file:.extra-dependencies/lbr-plutus-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "plutus-ledger-api": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + }, + "devDependencies": { + "@types/node": "^20.11.7", + "typescript": "^5.3.3" + } + }, + "node_modules/@types/node": { + "version": "20.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.7.tgz", + "integrity": "sha512-GPmeN1C3XAyV5uybAf4cMLWT9fDWcmQhZVtMFu7OR32WjrqGG+Wnk2V1d0bmtUyE/Zy1QJ9BxyiTih9z8Oks8A==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/lbf-plutus": { + "version": "1.0.0", + "resolved": "file:.extra-dependencies/lbf-plutus-1.0.0.tgz", + "integrity": "sha512-XtJBPw/Dh6KfkNXj/O7cQNkiuJcFhpfMm2G5HNSkLRq1+QAyjsyXicJwhO9toxzlQctaeyK9X0i3AaXJ965UaQ==", + "dependencies": { + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbr-plutus": "file:.extra-dependencies/lbr-plutus-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "plutus-ledger-api": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "node_modules/lbf-plutus-golden-api": { + "version": "1.0.0", + "resolved": "file:.extra-dependencies/lbf-plutus-golden-api-1.0.0.tgz", + "integrity": "sha512-OMIFtYu4L728HT8wYWqfvJK3rtEKUpY6xxO8eHZjfu0Dw0815XElPECaf/8FU7vijSIvuXWMudGpaE0Fgzx4RQ==", + "dependencies": { + "lbf-plutus": "file:.extra-dependencies/lbf-plutus-1.0.0.tgz", + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbr-plutus": "file:.extra-dependencies/lbr-plutus-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "plutus-ledger-api": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "node_modules/lbf-prelude": { + "version": "1.0.0", + "resolved": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "integrity": "sha512-ycFngZkq5iwwYROLBsvpTtw6BdhklgJkmIxWvmF0ksH6X462IuT7i9Rsvx37tfPOBNU6cHoUJ3PeUZHvj6VF3g==", + "dependencies": { + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "node_modules/lbr-plutus": { + "version": "1.0.0", + "resolved": "file:.extra-dependencies/lbr-plutus-1.0.0.tgz", + "integrity": "sha512-j7aS5tciyTIXbMK/azdOoKtAB9trUBi4DQ0kQZN1RfpspuOBePD7KLbAT64/oLtcKyqf2fSITk3B+IGawGj41w==", + "license": "ISC", + "dependencies": { + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "plutus-ledger-api": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "node_modules/lbr-prelude": { + "version": "1.0.0", + "resolved": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "integrity": "sha512-xakEfmSqbcYPCFeNFPAHdm2japkfw/a2Hh/kHeA+VZmEaKyVP8JrWJvtNWjfn/k7Dkbj0lbBl48LQWMFkqvjcQ==", + "license": "ISC", + "dependencies": { + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz", + "typescript": "^5.3.3" + } + }, + "node_modules/plutus-ledger-api": { + "version": "1.0.0", + "resolved": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "integrity": "sha512-UVQfoeULxTnLszKH6u1yYWiYXjqn6LulX02awhi0bX8Bvnoj0hF43EH3e+RrcMDe/6bQh+TVw7N2Xu+RU14esw==", + "license": "ISC", + "dependencies": { + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "node_modules/prelude": { + "version": "1.0.1", + "resolved": "file:.extra-dependencies/prelude-1.0.1.tgz", + "integrity": "sha512-NtSJIsn0+eDmzHZu3o5rK5Fh2Q3/eXmZ+tcsUsip7XmKl06RX0vTWWSsYngJhsdj+Xd7jZl/Wy92m8oNycykVA==", + "license": "ISC" + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + }, + "dependencies": { + "@types/node": { + "version": "20.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.7.tgz", + "integrity": "sha512-GPmeN1C3XAyV5uybAf4cMLWT9fDWcmQhZVtMFu7OR32WjrqGG+Wnk2V1d0bmtUyE/Zy1QJ9BxyiTih9z8Oks8A==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } + }, + "lbf-plutus": { + "version": "file:.extra-dependencies/lbf-plutus-1.0.0.tgz", + "integrity": "sha512-XtJBPw/Dh6KfkNXj/O7cQNkiuJcFhpfMm2G5HNSkLRq1+QAyjsyXicJwhO9toxzlQctaeyK9X0i3AaXJ965UaQ==", + "requires": { + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbr-plutus": "file:.extra-dependencies/lbr-plutus-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "plutus-ledger-api": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "lbf-plutus-golden-api": { + "version": "file:.extra-dependencies/lbf-plutus-golden-api-1.0.0.tgz", + "integrity": "sha512-OMIFtYu4L728HT8wYWqfvJK3rtEKUpY6xxO8eHZjfu0Dw0815XElPECaf/8FU7vijSIvuXWMudGpaE0Fgzx4RQ==", + "requires": { + "lbf-plutus": "file:.extra-dependencies/lbf-plutus-1.0.0.tgz", + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbr-plutus": "file:.extra-dependencies/lbr-plutus-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "plutus-ledger-api": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "lbf-prelude": { + "version": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "integrity": "sha512-ycFngZkq5iwwYROLBsvpTtw6BdhklgJkmIxWvmF0ksH6X462IuT7i9Rsvx37tfPOBNU6cHoUJ3PeUZHvj6VF3g==", + "requires": { + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "lbr-plutus": { + "version": "file:.extra-dependencies/lbr-plutus-1.0.0.tgz", + "integrity": "sha512-j7aS5tciyTIXbMK/azdOoKtAB9trUBi4DQ0kQZN1RfpspuOBePD7KLbAT64/oLtcKyqf2fSITk3B+IGawGj41w==", + "requires": { + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "plutus-ledger-api": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "lbr-prelude": { + "version": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "integrity": "sha512-xakEfmSqbcYPCFeNFPAHdm2japkfw/a2Hh/kHeA+VZmEaKyVP8JrWJvtNWjfn/k7Dkbj0lbBl48LQWMFkqvjcQ==", + "requires": { + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz", + "typescript": "^5.3.3" + } + }, + "plutus-ledger-api": { + "version": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "integrity": "sha512-UVQfoeULxTnLszKH6u1yYWiYXjqn6LulX02awhi0bX8Bvnoj0hF43EH3e+RrcMDe/6bQh+TVw7N2Xu+RU14esw==", + "requires": { + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "prelude": { + "version": "file:.extra-dependencies/prelude-1.0.1.tgz", + "integrity": "sha512-NtSJIsn0+eDmzHZu3o5rK5Fh2Q3/eXmZ+tcsUsip7XmKl06RX0vTWWSsYngJhsdj+Xd7jZl/Wy92m8oNycykVA==" + }, + "typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==" + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + } +} diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/package.json b/testsuites/lbt-plutus/lbt-plutus-typescript/package.json new file mode 100644 index 00000000..c92727cd --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/package.json @@ -0,0 +1,33 @@ +{ + "name": "lbf-prelude-typescript", + "version": "1.0.0", + "description": "Test suite project for LambdaBuffers", + "type": "module", + "exports": { + ".": "./dist/index.mjs", + "./package.json": "./package.json" + }, + "scripts": { + "build": "npx tsc -b src/", + "test": "node --test" + }, + "author": "Jared Pon", + "license": "ISC", + "files": [ + "./dist/**/*", + "./.extra-dependencies/**/*" + ], + "devDependencies": { + "@types/node": "^20.11.7", + "typescript": "^5.3.3" + }, + "dependencies": { + "lbf-plutus": "file:.extra-dependencies/lbf-plutus-1.0.0.tgz", + "lbf-plutus-golden-api": "file:.extra-dependencies/lbf-plutus-golden-api-1.0.0.tgz", + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbr-plutus": "file:.extra-dependencies/lbr-plutus-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "plutus-ledger-api": "file:.extra-dependencies/plutus-ledger-api-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } +} diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/src/Goldens.ts b/testsuites/lbt-plutus/lbt-plutus-typescript/src/Goldens.ts new file mode 100644 index 00000000..a80db459 --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/src/Goldens.ts @@ -0,0 +1,884 @@ +import * as LbrPrelude from "lbr-prelude"; +import * as LbrPlutusV1 from "lbr-plutus/V1.js"; +import * as LbrPlutusV2 from "lbr-plutus/V2.js"; + +import * as PlaV1 from "plutus-ledger-api/V1.js"; +import * as PlaMap from "plutus-ledger-api/AssocMap.js"; + +import * as LbfFooBar from "lbf-plutus-golden-api/LambdaBuffers/Foo/Bar.mjs"; +import * as LbfFoo from "lbf-plutus-golden-api/LambdaBuffers/Foo.mjs"; +import * as LbfDays from "lbf-plutus-golden-api/LambdaBuffers/Days.mjs"; + +/** + * Hard coded bytes for testing + */ +export function someBytes(): LbrPlutusV1.LedgerBytes { + return Uint8Array.from([115, 111, 109, 101, 32, 98, 121, 116, 101, 115]); +} + +/** + * Hard coded bytes for testing + */ +export function someMoreBytes(): LbrPlutusV1.LedgerBytes { + return Uint8Array.from([ + 115, + 111, + 109, + 101, + 32, + 109, + 111, + 114, + 101, + 32, + 98, + 121, + 116, + 101, + 115, + ]); +} + +/** + * Hard coded bytes for testing + */ +export function emptyBytes(): LbrPlutusV1.LedgerBytes { + return Uint8Array.from([]); +} + +/** + * Hard coded bytes for testing + */ +export function nullBytes(): LbrPlutusV1.LedgerBytes { + return Uint8Array.from([0]); +} + +/* + * Plutus.V1 goldens + */ + +/** + * Hard coded {@link PlutusData} goldens + */ +export function plutusDataGoldens(): LbrPrelude.List { + return [ + { name: "Constr", fields: [0n, []] }, + + { + name: "Constr", + fields: [1n, [ + { name: "Integer", fields: 1n }, + { name: "Bytes", fields: someBytes() }, + ]], + }, + { name: "List", fields: [] }, + + { + name: "List", + fields: [ + { name: "Integer", fields: 1n }, + { name: "Integer", fields: 2n }, + ], + }, + { + name: "List", + fields: [ + { name: "Integer", fields: 1n }, + { name: "Bytes", fields: someBytes() }, + ], + }, + { name: "Map", fields: PlaMap.fromList([]) }, + { + name: "Map", + fields: PlaMap.fromList([[{ name: "Integer", fields: 1n }, { + name: "Bytes", + fields: someBytes(), + }], [{ name: "Integer", fields: 2n }, { + name: "Bytes", + fields: someMoreBytes(), + }]]), + }, + { name: "Integer", fields: 0n }, + { name: "Integer", fields: 1n }, + { name: "Integer", fields: -1n }, + { name: "Bytes", fields: emptyBytes() }, + { name: "Bytes", fields: nullBytes() }, + { name: "Bytes", fields: someBytes() }, + ]; +} + +/** + * Hard coded bytes test + */ +export function blake2b_256Hash(): LbrPlutusV1.LedgerBytes { + const arr = []; + for (let i = 1; i <= 32; ++i) { + arr.push(i); + } + + return Uint8Array.from(arr); +} + +/** + * Hard coded bytes test + */ +export function blake2b_224Hash(): LbrPlutusV1.LedgerBytes { + const arr = []; + for (let i = 1; i <= 28; ++i) { + arr.push(i); + } + + return Uint8Array.from(arr); +} + +/** + * Hard coded {@link Address} test + */ +export function addressGoldens(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const credential of credentialGoldens()) { + const mStakingCredentials: LbrPrelude.List< + LbrPrelude.Maybe + > = [{ name: "Nothing" }]; + for (const mStakingCredential of mStakingCredentials) { + res.push({ + addressCredential: credential, + addressStakingCredential: mStakingCredential, + }); + } + } + + for (const credential of credentialGoldens()) { + for (const stakingCredential of stakingCredentialGoldens()) { + const mStakingCredential: LbrPrelude.Maybe< + LbrPlutusV1.StakingCredential + > = { name: "Just", fields: stakingCredential }; + res.push({ + addressCredential: credential, + addressStakingCredential: mStakingCredential, + }); + } + } + + return res; +} + +/** + * Hard coded {@link Credential} test + */ +export function credentialGoldens(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const pubKeyHash of pubKeyHashGoldens()) { + res.push({ name: "PubKeyCredential", fields: pubKeyHash }); + } + + for (const scriptHash of scriptHashGoldens()) { + res.push({ name: "ScriptCredential", fields: scriptHash }); + } + + return res; +} + +function unsafeFromJust(maybe: LbrPrelude.Maybe): A { + if (maybe.name === "Just") { + return maybe.fields; + } else { + throw new Error(`unsafeFromJust error: got Nothing but expected Just`); + } +} + +/** + * Hard coded {@link PubKeyHash} test + */ +export function pubKeyHashGoldens(): LbrPrelude.List { + return [unsafeFromJust(PlaV1.pubKeyHashFromBytes(blake2b_224Hash()))]; +} + +/** + * Hard coded {@link ScriptHash} test + */ +export function scriptHashGoldens(): LbrPrelude.List { + return [unsafeFromJust(PlaV1.scriptHashFromBytes(blake2b_224Hash()))]; +} + +/** + * Hard coded {@link StakingCredential} test + */ +export function stakingCredentialGoldens(): LbrPrelude.List< + LbrPlutusV1.StakingCredential +> { + const res: LbrPrelude.List = []; + + for (const credential of credentialGoldens()) { + res.push({ name: "StakingHash", fields: credential }); + } + + res.push({ name: "StakingPtr", fields: [0n, 1n, 2n] }); + + return res; +} + +/** + * Hard coded {@link LedgerBytes} tests + */ +export function bytesGoldens(): LbrPrelude.List { + return [emptyBytes(), nullBytes(), someBytes()]; +} + +/** + * Hard coded {@link Interval} tests + */ +export function intervalGoldens(): LbrPrelude.List< + LbrPlutusV1.Interval +> { + const res: LbrPrelude.List> = []; + + for (const lb of lowerBoundGoldens()) { + for (const ub of upperBoundGoldens()) { + res.push({ ivFrom: lb, ivTo: ub }); + } + } + + return res; +} + +/** + * Hard coded {@link LowerBound} tests + */ +export function lowerBoundGoldens(): LbrPrelude.List< + LbrPlutusV1.LowerBound +> { + const res: LbrPrelude.List> = + []; + + for (const extended of extendedGoldens()) { + for (const closure of closureGoldens()) { + res.push([extended, closure]); + } + } + + return res; +} + +/** + * Hard coded {@link UpperBound} tests + */ +export function upperBoundGoldens(): LbrPrelude.List< + LbrPlutusV1.UpperBound +> { + const res: LbrPrelude.List> = + []; + + for (const extended of extendedGoldens()) { + for (const closure of closureGoldens()) { + res.push([extended, closure]); + } + } + + return res; +} + +/** + * Hard coded {@link Extended} tests + */ +export function extendedGoldens(): LbrPrelude.List< + LbrPlutusV1.Extended +> { + return [ + { name: "NegInf" }, + { name: "PosInf" }, + { + name: "Finite", + fields: 0n, + }, + ]; +} + +/** + * Hard coded {@link Closure} tests + */ +export function closureGoldens(): LbrPrelude.List { + return [true, false]; +} + +/** + * Hard coded {@link POSIXTime} tests + */ +export function posixTimeGoldens(): LbrPrelude.List { + return [0n, 1n, 2n]; +} + +/** + * Hard coded {@link POSIXTimeRange} tests + */ +export function posixTimeRangeGoldens(): LbrPrelude.List< + LbrPlutusV1.POSIXTimeRange +> { + return intervalGoldens(); +} + +/** + * Hard coded {@link CurrencySymbol} tests + */ +export function currencySymbolGoldens(): LbrPrelude.List< + LbrPlutusV1.CurrencySymbol +> { + return [unsafeFromJust(PlaV1.currencySymbolFromBytes(blake2b_224Hash()))]; +} + +/** + * Hard coded ada {@link CurrencySymbol} test + */ +export function adaCurrencySymbolGolden(): LbrPlutusV1.CurrencySymbol { + return PlaV1.adaSymbol; +} + +/** + * Hard coded {@link TokenName} tests + */ +export function tokenNameGoldens(): LbrPrelude.List { + const tn1 = unsafeFromJust(PlaV1.tokenNameFromBytes(emptyBytes())); + const tn2 = unsafeFromJust(PlaV1.tokenNameFromBytes( + ((arr: LbrPrelude.List) => { + for (let i = 1; i < 16; ++i) { + arr.push(i); + } + return Uint8Array.from(arr); + })([]), + )); + const tn3 = unsafeFromJust(PlaV1.tokenNameFromBytes( + ((arr: LbrPrelude.List) => { + for (let i = 1; i < 32; ++i) { + arr.push(i); + } + return Uint8Array.from(arr); + })([]), + )); + + return [tn1, tn2, tn3]; +} + +/** + * Hard coded {@link AssetClass} tests + */ +export function assetClassGoldens(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const currencySymbol of currencySymbolGoldens()) { + for (const tokenName of tokenNameGoldens()) { + res.push([currencySymbol, tokenName]); + } + } + + res.push([PlaV1.adaSymbol, PlaV1.adaToken]); + + return res; +} + +/** + * Hard coded {@link Value} tests + */ +export function valueGoldens(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const map of mapGoldens()) { + res.push(map); + } + + return res; +} + +/** + * Hard coded {@link Map} tests + */ +export function mapGoldens(): LbrPrelude.List< + LbrPlutusV1.Map< + LbrPlutusV1.CurrencySymbol, + LbrPlutusV1.Map + > +> { + return [ + PlaMap.fromList([]), + PlaMap.fromList< + LbrPlutusV1.CurrencySymbol, + LbrPlutusV1.Map + >( + [ + [ + PlaV1.adaSymbol, + PlaMap.fromList( + [[PlaV1.adaToken, 1337n]], + ), + ], + ], + ), + PlaMap.fromList< + LbrPlutusV1.CurrencySymbol, + LbrPlutusV1.Map + >( + [ + [ + PlaV1.adaSymbol, + PlaMap.fromList([[ + PlaV1.adaToken, + 1337n, + ]]), + ], + + [ + unsafeFromJust(PlaV1.currencySymbolFromBytes(blake2b_224Hash())), + PlaMap.fromList( + [ + [unsafeFromJust(PlaV1.tokenNameFromBytes(emptyBytes())), 1337n], + [ + unsafeFromJust(PlaV1.tokenNameFromBytes( + ((arr: LbrPrelude.List) => { + for (let i = 1; i < 16; ++i) { + arr.push(i); + } + return Uint8Array.from(arr); + })([]), + )), + 16n, + ], + [ + unsafeFromJust(PlaV1.tokenNameFromBytes( + ((arr: LbrPrelude.List) => { + for (let i = 1; i < 32; ++i) { + arr.push(i); + } + return Uint8Array.from(arr); + })([]), + )), + 32n, + ], + ], + ), + ], + ], + ), + ]; +} + +/** + * Hard coded {@link Redeemer} tests + */ +export function redeemerGoldens(): LbrPrelude.List { + return [ + { name: "Integer", fields: 1337n }, + ]; +} + +/** + * Hard coded {@link Datum} tests + */ +export function datumGoldens(): LbrPrelude.List { + return [ + { name: "Integer", fields: 1337n }, + ]; +} + +/** + * Hard coded {@link RedeemerHash} tests + */ +export function redeemerHashGoldens(): LbrPrelude.List< + LbrPlutusV1.RedeemerHash +> { + return [ + unsafeFromJust(PlaV1.redeemerHashFromBytes(blake2b_256Hash())), + ]; +} + +/** + * Hard coded {@link DatumHash} tests + */ +export function datumHashGoldens(): LbrPrelude.List { + return [ + unsafeFromJust(PlaV1.datumHashFromBytes(blake2b_256Hash())), + ]; +} + +/** + * Hard coded {@link TxId} tests + */ +export function txIdGoldens(): LbrPrelude.List { + return [ + unsafeFromJust(PlaV1.txIdFromBytes(blake2b_256Hash())), + ]; +} + +/** + * Hard coded {@link TxOutRef} tests + */ +export function txOutRefGoldens(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const txId of txIdGoldens()) { + for (const txIdx of [0n]) { + res.push({ txOutRefId: txId, txOutRefIdx: txIdx }); + } + } + + return res; +} + +// /* +// * Hard coded {@link TxInInfo} tests +// * TODO(jaredponn): this is borked -- this type doesn't actually exist in the .lbf file +// */ +// export function txInInfoGoldensV1() : LbrPrelude.List { +// const res : LbrPrelude.List = []; +// +// for (const txOutRef of txOutRefGoldens()) { +// for (const txOut of txOutGoldensV1()) { +// } +// } +// return res +// } + +// /* +// * Hard coded {@link TxOut} tests +// * TODO(jaredponn): this is borked -- this type doesn't actually exist in the .lbf file +// */ +// + +/* + * Plutus.V2 goldens + */ + +/** + * Hard coded {@link TxInInfo} tests + */ +export function txInInfoGoldensV2(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const txOutRef of txOutRefGoldens()) { + for (const txOut of txOutGoldensV2()) { + res.push({ txInInfoOutRef: txOutRef, txInInfoResolved: txOut }); + } + } + + return res; +} + +/** + * Hard coded {@link TxOut} tests + */ +export function txOutGoldensV2(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const address of addressGoldens()) { + for (const value of valueGoldens()) { + const outDatums = outDatumGoldens(); + for (let outDatumIx = 0; outDatumIx < 1; ++outDatumIx) { + const mScriptHashes: LbrPrelude.List< + LbrPrelude.Maybe + > = [{ name: "Nothing" }]; + mScriptHashes.concat( + scriptHashGoldens().map((x) => { + return { fields: x, name: "Just" }; + }), + ); + + for (const mScriptHash of mScriptHashes) { + res.push( + { + txOutAddress: address, + txOutValue: value, + txOutDatum: outDatums[outDatumIx]!, + txOutReferenceScript: mScriptHash, + }, + ); + } + } + } + } + + return res; +} + +/** + * Hard coded {@link OutputDatum} tests + */ +export function outDatumGoldens(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + res.push({ name: "NoOutputDatum" }); + + for (const datumHash of datumHashGoldens()) { + res.push({ name: "OutputDatumHash", fields: datumHash }); + } + + for (const outputDatum of datumGoldens()) { + res.push({ name: "OutputDatum", fields: outputDatum }); + } + + return res; +} + +/* + * Foo.Bar goldens + */ + +/** + * Hardcoded {@link FooSum} tests + */ +export function fooSumGoldens( + x: A, + y: B, + z: C, +): LbrPrelude.List> { + return [ + { name: "Foo", fields: [x, y, z] }, + { name: "Bar", fields: [x, y] }, + { name: "Baz", fields: y }, + { name: "Qax" }, + { name: "Faz", fields: 0n }, + ]; +} + +/** + * Hardcoded {@link FooProd} tests + */ +export function fooProdGoldens( + x: A, + y: B, + z: C, +): LbfFooBar.FooProd[] { + return [[x, y, z, 1337n]]; +} + +/** + * Hard coded {@link FooRec} tests + */ +export function fooRecGoldens( + x: A, + y: B, + z: C, +): LbfFooBar.FooRec[] { + return [ + { fooA: x, fooB: y, fooC: z, fooInt: 1337n }, + ]; +} + +/* + * Foo goldens + */ + +/** + * Hard coded {@link A} tests + */ +export function aGoldens(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const address of addressGoldens()) { + for (const value of valueGoldens()) { + for (const datum of datumGoldens()) { + for (const fooSum of fooSumGoldens(address, value, datum)) { + res.push(fooSum); + } + } + } + } + return res; +} + +/** + * Hard coded {@link B} tests + */ +export function bGoldens(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const address of addressGoldens()) { + for (const value of valueGoldens()) { + for (const datum of datumGoldens()) { + for (const fooProd of fooProdGoldens(address, value, datum)) { + res.push(fooProd); + } + } + } + } + return res; +} + +/** + * Hard coded {@link C} tests + */ +export function cGoldens(): LbrPrelude.List { + const res: LbrPrelude.List = []; + + for (const address of addressGoldens()) { + for (const value of valueGoldens()) { + for (const datum of datumGoldens()) { + for (const fooRec of fooRecGoldens(address, value, datum)) { + res.push(fooRec); + } + } + } + } + return res; +} + +/** + * Hard coded {@link D} tests + */ +export function dGoldens(): LbrPrelude.List { + let fooSums: LbrPrelude.List< + LbfFooBar.FooSum + > = []; + + for (const address of addressGoldens()) { + for (const value of valueGoldens()) { + for (const datum of datumGoldens()) { + for (const fooSum of fooSumGoldens(address, value, datum)) { + fooSums.push(fooSum); + } + } + } + } + + fooSums = fooSums.slice(0, 2); + + let fooProds: LbrPrelude.List< + LbfFooBar.FooProd + > = []; + + for (const address of addressGoldens()) { + for (const value of valueGoldens()) { + for (const datum of datumGoldens()) { + for (const fooProd of fooProdGoldens(address, value, datum)) { + fooProds.push(fooProd); + } + } + } + } + + fooProds = fooProds.slice(0, 2); + + let fooRecs: LbrPrelude.List< + LbfFooBar.FooRec + > = []; + + for (const address of addressGoldens()) { + for (const value of valueGoldens()) { + for (const datum of datumGoldens()) { + for (const fooRec of fooRecGoldens(address, value, datum)) { + fooRecs.push(fooRec); + } + } + } + } + + fooRecs = fooRecs.slice(0, 2); + + const fooComplicateds: LbrPrelude.List = []; + for (const fooSum of fooSums) { + for (const fooProd of fooProds) { + for (const fooRec of fooRecs) { + fooComplicateds.push( + { sum: fooSum, prod: fooProd, rec: fooRec }, + ); + } + } + } + + return fooComplicateds; +} + +/** + * Hard coded {@link FInt} tests + */ +export function fIntGoldens(): LbfFoo.FInt[] { + return [ + { name: "Nil" }, + { name: "Rec", fields: { name: "Rec", fields: { name: "Nil" } } }, + ]; +} + +/** + * Hard coded {@link GInt} tests + */ +export function gIntGoldens(): LbfFoo.GInt[] { + return [ + { name: "Nil" }, + { name: "Rec", fields: { name: "Rec", fields: { name: "Nil" } } }, + ]; +} + +/* + * Days goldens + */ + +/** + * Hard coded {@link Day} tests + */ +export function dayGoldens(): LbfDays.Day[] { + return [ + { name: "Monday" }, + { name: "Tuesday" }, + { name: "Wednesday" }, + { name: "Thursday" }, + { name: "Friday" }, + { name: "Saturday" }, + { name: "Sunday" }, + ]; +} + +/** + * Hard coded {@link WorkDay} tests + */ +export function workDayGoldens(): LbfDays.WorkDay[] { + return [{ name: "Monday" }, { name: "Tuesday" }, { name: "Wednesday" }, { + name: "Thursday", + }, { name: "Friday" }]; +} + +/** + * Hard coded {@link FreeDay} tests + */ +export function freeDayGoldens(): LbfDays.FreeDay[] { + return [{ day: { name: "Saturday" } }, { day: { name: "Sunday" } }]; +} + +/* + * Prelude goldens + */ + +/** + * Hard coded {@link Bool} tests + */ +export function boolGoldens(): LbrPrelude.Bool[] { + return [false, true]; +} + +/** + * Hard coded {@link Maybe} tests + */ +export function maybeGoldens(): LbrPrelude.Maybe[] { + return [{ name: "Nothing" }, { name: "Just", fields: true }, { + name: "Just", + fields: false, + }]; +} + +/** + * Hard coded {@link Either} tests + */ +export function eitherGoldens(): LbrPrelude.Either< + LbrPrelude.Bool, + LbrPrelude.Bool +>[] { + return [{ name: "Left", fields: true }, { name: "Left", fields: false }, { + name: "Right", + fields: true, + }]; +} + +/** + * Hard coded {@link List} tests + */ +export function listGoldens(): LbrPrelude.List[] { + return [[], [true], [false], [true, true, false, false]]; +} diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/src/Json-test.ts b/testsuites/lbt-plutus/lbt-plutus-typescript/src/Json-test.ts new file mode 100644 index 00000000..29031606 --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/src/Json-test.ts @@ -0,0 +1,480 @@ +import { describe, it } from "node:test"; + +import * as Utils from "./Utils.js"; + +import * as Goldens from "./Goldens.js"; + +import * as LbrPrelude from "lbr-prelude"; +import * as PreludeJson from "prelude/Json.js"; + +import * as LbrPlutusV1 from "lbr-plutus/V1.js"; +import * as LbrPlutusV2 from "lbr-plutus/V2.js"; + +describe("JSON tests (toJson . fromJson)", () => { + const goldenDir = `data/lbt-plutus-golden-data`; + + it(`PlutusV1.PlutusData from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.PlutusData\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson, + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson, + PreludeJson.stringify, + ), + Goldens.plutusDataGoldens(), + ); + }); + + it(`PlutusV1.Address from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Address\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.Address].fromJson, + LbrPrelude.Json[LbrPlutusV1.Address].toJson, + PreludeJson.stringify, + ), + Goldens.addressGoldens(), + ); + }); + + it(`PlutusV1.Credential from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Credential\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.Credential].fromJson, + LbrPrelude.Json[LbrPlutusV1.Credential].toJson, + PreludeJson.stringify, + ), + Goldens.credentialGoldens(), + ); + }); + + it(`PlutusV1.StakingCredential from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.StakingCredential\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.StakingCredential].fromJson, + LbrPrelude.Json[LbrPlutusV1.StakingCredential].toJson, + PreludeJson.stringify, + ), + Goldens.stakingCredentialGoldens(), + ); + }); + + it(`PlutusV1.PubKeyHash from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.PubKeyHash\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.PubKeyHash].fromJson, + LbrPrelude.Json[LbrPlutusV1.PubKeyHash].toJson, + PreludeJson.stringify, + ), + Goldens.pubKeyHashGoldens(), + ); + }); + + it(`PlutusV1.Bytes from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Bytes\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.LedgerBytes].fromJson, + LbrPrelude.Json[LbrPlutusV1.LedgerBytes].toJson, + PreludeJson.stringify, + ), + Goldens.bytesGoldens(), + ); + }); + + it(`PlutusV1.Interval from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Interval\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.Interval]( + LbrPrelude.Json[LbrPlutusV1.POSIXTime], + ).fromJson, + LbrPrelude.Json[LbrPlutusV1.Interval]( + LbrPrelude.Json[LbrPlutusV1.POSIXTime], + ).toJson, + PreludeJson.stringify, + ), + Goldens.intervalGoldens(), + ); + }); + + it(`PlutusV1.Extended from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Extended\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.Extended]( + LbrPrelude.Json[LbrPlutusV1.POSIXTime], + ).fromJson, + LbrPrelude.Json[LbrPlutusV1.Extended]( + LbrPrelude.Json[LbrPlutusV1.POSIXTime], + ).toJson, + PreludeJson.stringify, + ), + Goldens.extendedGoldens(), + ); + }); + + it(`PlutusV1.LowerBound from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.LowerBound\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.LowerBound]( + LbrPrelude.Json[LbrPlutusV1.POSIXTime], + ).fromJson, + LbrPrelude.Json[LbrPlutusV1.LowerBound]( + LbrPrelude.Json[LbrPlutusV1.POSIXTime], + ).toJson, + PreludeJson.stringify, + ), + Goldens.lowerBoundGoldens(), + ); + }); + + it(`PlutusV1.UpperBound from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.UpperBound\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.UpperBound]( + LbrPrelude.Json[LbrPlutusV1.POSIXTime], + ).fromJson, + LbrPrelude.Json[LbrPlutusV1.UpperBound]( + LbrPrelude.Json[LbrPlutusV1.POSIXTime], + ).toJson, + PreludeJson.stringify, + ), + Goldens.upperBoundGoldens(), + ); + }); + + it(`PlutusV1.POSIXTime from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.POSIXTime\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.POSIXTime].fromJson, + LbrPrelude.Json[LbrPlutusV1.POSIXTime].toJson, + PreludeJson.stringify, + ), + Goldens.posixTimeGoldens(), + ); + }); + + it(`PlutusV1.POSIXTimeRange from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.POSIXTimeRange\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.POSIXTimeRange].fromJson, + LbrPrelude.Json[LbrPlutusV1.POSIXTimeRange].toJson, + PreludeJson.stringify, + ), + Goldens.posixTimeRangeGoldens(), + ); + }); + + it(`PlutusV1.CurrencySymbol from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.CurrencySymbol\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.CurrencySymbol].fromJson, + LbrPrelude.Json[LbrPlutusV1.CurrencySymbol].toJson, + PreludeJson.stringify, + ), + [Goldens.adaCurrencySymbolGolden()].concat( + Goldens.currencySymbolGoldens(), + ), + ); + }); + + it(`PlutusV1.TokenName from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.TokenName\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.TokenName].fromJson, + LbrPrelude.Json[LbrPlutusV1.TokenName].toJson, + PreludeJson.stringify, + ), + Goldens.tokenNameGoldens(), + ); + }); + + it(`PlutusV1.AssetClass from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.AssetClass\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.AssetClass].fromJson, + LbrPrelude.Json[LbrPlutusV1.AssetClass].toJson, + PreludeJson.stringify, + ), + Goldens.assetClassGoldens(), + ); + }); + + it(`PlutusV1.Value from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Value\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.Value].fromJson, + LbrPrelude.Json[LbrPlutusV1.Value].toJson, + PreludeJson.stringify, + ), + Goldens.valueGoldens(), + ); + }); + + it(`PlutusV1.Redeemer from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Redeemer\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.Redeemer].fromJson, + LbrPrelude.Json[LbrPlutusV1.Redeemer].toJson, + PreludeJson.stringify, + ), + Goldens.redeemerGoldens(), + ); + }); + + it(`PlutusV1.Datum from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Datum\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.Datum].fromJson, + LbrPrelude.Json[LbrPlutusV1.Datum].toJson, + PreludeJson.stringify, + ), + Goldens.datumGoldens(), + ); + }); + + it(`PlutusV1.RedeemerHash from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.RedeemerHash\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.RedeemerHash].fromJson, + LbrPrelude.Json[LbrPlutusV1.RedeemerHash].toJson, + PreludeJson.stringify, + ), + Goldens.redeemerHashGoldens(), + ); + }); + + it(`PlutusV1.DatumHash from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.DatumHash\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.DatumHash].fromJson, + LbrPrelude.Json[LbrPlutusV1.DatumHash].toJson, + PreludeJson.stringify, + ), + Goldens.datumHashGoldens(), + ); + }); + + it(`PlutusV1.ScriptHash from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.ScriptHash\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.ScriptHash].fromJson, + LbrPrelude.Json[LbrPlutusV1.ScriptHash].toJson, + PreludeJson.stringify, + ), + Goldens.scriptHashGoldens(), + ); + }); + + it(`PlutusV1.TxId from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.TxId\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.TxId].fromJson, + LbrPrelude.Json[LbrPlutusV1.TxId].toJson, + PreludeJson.stringify, + ), + Goldens.txIdGoldens(), + ); + }); + + it(`PlutusV1.TxOutRef from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.TxOutRef\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.TxOutRef].fromJson, + LbrPrelude.Json[LbrPlutusV1.TxOutRef].toJson, + PreludeJson.stringify, + ), + Goldens.txOutRefGoldens(), + ); + }); + + it(`PlutusV1.Map from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Map\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV1.Map]( + LbrPrelude.Json[LbrPlutusV1.CurrencySymbol], + LbrPrelude.Json[LbrPlutusV1.Map]( + LbrPrelude.Json[LbrPlutusV1.TokenName], + LbrPrelude.Json[LbrPrelude.Integer], + ), + ).fromJson, + LbrPrelude.Json[LbrPlutusV1.Map]( + LbrPrelude.Json[LbrPlutusV1.CurrencySymbol], + LbrPrelude.Json[LbrPlutusV1.Map]( + LbrPrelude.Json[LbrPlutusV1.TokenName], + LbrPrelude.Json[LbrPrelude.Integer], + ), + ).toJson, + PreludeJson.stringify, + ), + Goldens.mapGoldens(), + ); + }); + + // TODO(jaredponn): TxInInfo V1 doesn't exist yet + // TODO(jaredponn): TxOut V1 doesn't exist yet + + it(`PlutusV2.TxInInfo from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV2\.TxInInfo\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV2.TxInInfo].fromJson, + LbrPrelude.Json[LbrPlutusV2.TxInInfo].toJson, + PreludeJson.stringify, + ), + Goldens.txInInfoGoldensV2(), + ); + }); + + it(`PlutusV2.OutputDatum from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV2\.OutputDatum\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV2.OutputDatum].fromJson, + LbrPrelude.Json[LbrPlutusV2.OutputDatum].toJson, + PreludeJson.stringify, + ), + Goldens.outDatumGoldens(), + ); + }); + + it(`PlutusV2.TxOut from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV2\.TxOut\.[0-9]*\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + LbrPrelude.Json[LbrPlutusV2.TxOut].fromJson, + LbrPrelude.Json[LbrPlutusV2.TxOut].toJson, + PreludeJson.stringify, + ), + Goldens.txOutGoldensV2(), + ); + }); +}); diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/src/PlutusData-test.ts b/testsuites/lbt-plutus/lbt-plutus-typescript/src/PlutusData-test.ts new file mode 100644 index 00000000..4a71ae3c --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/src/PlutusData-test.ts @@ -0,0 +1,1051 @@ +import { describe, it } from "node:test"; + +import * as Utils from "./Utils.js"; + +import * as Goldens from "./Goldens.js"; + +import * as LbrPrelude from "lbr-prelude"; +import * as PreludeJson from "prelude/Json.js"; + +import * as LbfFoo from "lbf-plutus-golden-api/LambdaBuffers/Foo.mjs"; +import * as LbfDays from "lbf-plutus-golden-api/LambdaBuffers/Days.mjs"; + +import * as LbrPlutusV1 from "lbr-plutus/V1.js"; +import * as LbrPlutusV2 from "lbr-plutus/V2.js"; + +/** + * Loosely, we're testing something along the lines of: + * ``` + * toJson . toPlutusData . fromPlutusData . fromJson + * ``` + */ +describe("PlutusData tests (toJson . toPlutusData . fromPlutusData . fromJson)", () => { + const goldenDir = `data/lbt-plutus-golden-data`; + + describe("Foo tests", () => { + it(`Foo.A from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Foo\.A\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbfFoo.A] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbfFoo.A] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.aGoldens(), + ); + }); + + it(`Foo.B from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Foo\.B\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbfFoo.B] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbfFoo.B] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.bGoldens(), + ); + }); + + it(`Foo.C from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Foo\.C\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbfFoo.C] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbfFoo.C] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.cGoldens(), + ); + }); + + it(`Foo.D from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Foo\.D\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbfFoo.D] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbfFoo.D] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.dGoldens(), + ); + }); + + it(`Foo.FInt from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Foo\.FInt\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbfFoo.FInt] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbfFoo.FInt] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.fIntGoldens(), + ); + }); + + it(`Foo.GInt from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Foo\.GInt\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbfFoo.GInt] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbfFoo.GInt] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.gIntGoldens(), + ); + }); + }); + + describe("Day tests", () => { + it(`Days.Day from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Days\.Day\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbfDays.Day] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbfDays.Day] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.dayGoldens(), + ); + }); + + it(`Days.WorkDay from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Days\.WorkDay\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbfDays.WorkDay] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbfDays.WorkDay] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.workDayGoldens(), + ); + }); + + it(`Days.FreeDay from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Days\.FreeDay\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbfDays.FreeDay] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbfDays.FreeDay] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.freeDayGoldens(), + ); + }); + }); + + describe("Plutus tests", () => { + it(`PlutusV1.PlutusData from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.PlutusData\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.PlutusData] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.PlutusData] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.plutusDataGoldens(), + ); + }); + + it(`PlutusV1.Address from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Address\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Address] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Address] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.addressGoldens(), + ); + }); + + it(`PlutusV1.Credential from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Credential\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Credential] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Credential] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.credentialGoldens(), + ); + }); + + it(`PlutusV1.StakingCredential from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.StakingCredential\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.StakingCredential] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.StakingCredential] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.stakingCredentialGoldens(), + ); + }); + + it(`PlutusV1.PubKeyHash from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.PubKeyHash\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.PubKeyHash] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.PubKeyHash] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.pubKeyHashGoldens(), + ); + }); + + it(`PlutusV1.Bytes from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Bytes\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.LedgerBytes] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.LedgerBytes] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.bytesGoldens(), + ); + }); + + it(`PlutusV1.Interval from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Interval\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Interval]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.POSIXTime], + ) + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Interval]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.POSIXTime], + ) + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.intervalGoldens(), + ); + }); + + it(`PlutusV1.Extended from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Extended\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Extended]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.POSIXTime], + ) + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Extended]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.POSIXTime], + ) + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.extendedGoldens(), + ); + }); + + it(`PlutusV1.LowerBound from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.LowerBound\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.LowerBound]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.POSIXTime], + ) + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.LowerBound]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.POSIXTime], + ) + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.lowerBoundGoldens(), + ); + }); + + it(`PlutusV1.UpperBound from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.UpperBound\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.UpperBound]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.POSIXTime], + ) + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.UpperBound]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.POSIXTime], + ) + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.upperBoundGoldens(), + ); + }); + + it(`PlutusV1.POSIXTime from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.POSIXTime\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.POSIXTime] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.POSIXTime] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.posixTimeGoldens(), + ); + }); + + it(`PlutusV1.POSIXTimeRange from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.POSIXTimeRange\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.POSIXTimeRange] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.POSIXTimeRange] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.posixTimeRangeGoldens(), + ); + }); + + it(`PlutusV1.CurrencySymbol from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.CurrencySymbol\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.CurrencySymbol] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.CurrencySymbol] + .toData(v), + ), + PreludeJson.stringify, + ), + [Goldens.adaCurrencySymbolGolden()].concat( + Goldens.currencySymbolGoldens(), + ), + ); + }); + + it(`PlutusV1.TokenName from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.TokenName\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.TokenName] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.TokenName] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.tokenNameGoldens(), + ); + }); + + it(`PlutusV1.AssetClass from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.AssetClass\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.AssetClass] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.AssetClass] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.assetClassGoldens(), + ); + }); + + it(`PlutusV1.Value from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Value\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Value] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Value] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.valueGoldens(), + ); + }); + + it(`PlutusV1.Redeemer from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Redeemer\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Redeemer] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Redeemer] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.redeemerGoldens(), + ); + }); + + it(`PlutusV1.Datum from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Datum\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Datum] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.Datum] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.datumGoldens(), + ); + }); + + it(`PlutusV1.RedeemerHash from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.RedeemerHash\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.RedeemerHash] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.RedeemerHash] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.redeemerHashGoldens(), + ); + }); + + it(`PlutusV1.DatumHash from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.DatumHash\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.DatumHash] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.DatumHash] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.datumHashGoldens(), + ); + }); + + it(`PlutusV1.ScriptHash from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.ScriptHash\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.ScriptHash] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.ScriptHash] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.scriptHashGoldens(), + ); + }); + + it(`PlutusV1.TxId from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.TxId\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.TxId] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.TxId] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.txIdGoldens(), + ); + }); + + it(`PlutusV1.TxOutRef from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.TxOutRef\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.TxOutRef] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV1.TxOutRef] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.txOutRefGoldens(), + ); + }); + + it(`PlutusV1.Map from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV1\.Map\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1.IsPlutusData[LbrPlutusV1.Map]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.CurrencySymbol], + LbrPlutusV1.IsPlutusData[LbrPlutusV1.Map]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.TokenName], + LbrPlutusV1.IsPlutusData[LbrPrelude.Integer], + ), + ) + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.Map]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.CurrencySymbol], + LbrPlutusV1.IsPlutusData[LbrPlutusV1.Map]( + LbrPlutusV1.IsPlutusData[LbrPlutusV1.TokenName], + LbrPlutusV1.IsPlutusData[LbrPrelude.Integer], + ), + ) + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.mapGoldens(), + ); + }); + + // TODO(jaredponn) TxInInfo V1 missing + // TODO(jaredponn) TxOut V1 missing + + it(`PlutusV2.TxInInfo from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV2\.TxInInfo\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV2.TxInInfo] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV2.TxInInfo] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.txInInfoGoldensV2(), + ); + }); + + it(`PlutusV2.OutputDatum from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV2\.OutputDatum\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV2.OutputDatum] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV2.OutputDatum] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.outDatumGoldens(), + ); + }); + + it(`PlutusV2.TxOut from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^PlutusV2\.TxOut\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPlutusV2.TxOut] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData].fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPlutusV2.TxOut] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.txOutGoldensV2(), + ); + }); + }); + + describe("Prelude tests", () => { + it(`Prelude.Bool from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Prelude\.Bool\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPrelude.Bool] + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPrelude.Bool] + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.boolGoldens(), + ); + }); + + it(`Prelude.Maybe from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Prelude\.Maybe\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPrelude.Maybe]( + LbrPlutusV1.IsPlutusData[LbrPrelude.Bool], + ) + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPrelude.Maybe]( + LbrPlutusV1.IsPlutusData[LbrPrelude.Bool], + ) + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.maybeGoldens(), + ); + }); + + it(`Prelude.Either from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Prelude\.Either\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPrelude.Either]( + LbrPlutusV1.IsPlutusData[LbrPrelude.Bool], + LbrPlutusV1.IsPlutusData[LbrPrelude.Bool], + ) + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPrelude.Either]( + LbrPlutusV1.IsPlutusData[LbrPrelude.Bool], + LbrPlutusV1.IsPlutusData[LbrPrelude.Bool], + ) + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.eitherGoldens(), + ); + }); + + it(`Prelude.List from to golden tests`, async () => { + await Utils.fromToGoldenTest( + goldenDir, + new Utils.RegExpFileFilter( + /^Prelude\.List\.[0-9]*\.pd\.json$/g, + ), + Utils.mkFromToAssertGolden( + PreludeJson.parseJson, + (v) => + LbrPlutusV1 + .IsPlutusData[LbrPrelude.List]( + LbrPlutusV1.IsPlutusData[LbrPrelude.Bool], + ) + .fromData(LbrPrelude.Json[LbrPlutusV1.PlutusData] + .fromJson(v)), + (v) => + LbrPrelude.Json[LbrPlutusV1.PlutusData].toJson( + LbrPlutusV1 + .IsPlutusData[LbrPrelude.List]( + LbrPlutusV1.IsPlutusData[LbrPrelude.Bool], + ) + .toData(v), + ), + PreludeJson.stringify, + ), + Goldens.listGoldens(), + ); + }); + }); +}); diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/src/Utils.ts b/testsuites/lbt-plutus/lbt-plutus-typescript/src/Utils.ts new file mode 100644 index 00000000..62efab0e --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/src/Utils.ts @@ -0,0 +1,137 @@ +import { it } from "node:test"; +import * as assert from "node:assert/strict"; +import * as Fs from "node:fs/promises"; +import * as Path from "node:path"; + +// WARNING(jaredponn): {@link findGoldens} and {@link fromToGoldenTest} are +// essentially duplicated code (well minor generalizations) of the same testing +// functions in the lbt-prelude testsuite. + +/** +/* @param goldenDir: directory for the golden files +/* @param regexFileFilter: see {@link RegExpFileFilter}. + * +/* @returns Tuple of [path to golden file, the resulting "regexFileFilter"ed file] + */ +export async function findGoldens( + goldenDir: string, + regexFileFilter: RegExpFileFilter, +): Promise<[string, string][]> { + const files: string[] = await Fs.readdir(goldenDir, { recursive: true }); + + const filteredFiles: [string, string][] = []; + for (const file of files) { + const filtered = regexFileFilter.match(file); + if (typeof filtered === "string") { + filteredFiles.push( + [Path.join(goldenDir, file), filtered], + ); + } + } + return filteredFiles; +} + +/** + * Wraps a regular expression s.t. when matching strings, we match + * `basename().match()` + */ +export class RegExpFileFilter { + #regexp: RegExp; + + /** + * @param `.match(regexp)` must either match with exactly one string, or + * not match at all. + */ + constructor(regexp: Readonly) { + this.#regexp = regexp; + } + + match(filepath: string): string | null { + const matches = Path.basename(filepath).match(this.#regexp); + if (matches === null) { + return null; + } else if (matches.length === 1) { + return matches[0]; + } else { + throw new Error( + `RegExpFileFilter: regex \`${this.#regexp}\` produced matches \`${matches}\` for \`${filepath}\`, but should only produce exactly one match`, + ); + } + } + + toString(): string { + return this.#regexp.toString(); + } +} + +/** + * Runs golden tests in the provided `goldenDir` which satisfy the + * `regexFileFilter` where the test passes if `assertGolden` does not throw an + * exception. Note that `goldens` is essentially unused and is only used to + * warn if the number of the TS representation of equivalent HS generated tests + * match. + */ +export async function fromToGoldenTest( + goldenDir: string, + regexFileFilter: RegExpFileFilter, + // assertGolden: function which asserts whether the test is valid. + // index: golden test file name (excluding extension) + // content: the contents of the golden test file (assumed to be UTF8 encoded + // file). + assertGolden: (index: string, content: string) => void, + goldens: A[], +): Promise { + const foundGoldens: [string, string][] = await findGoldens( + goldenDir, + regexFileFilter, + ); + + if (foundGoldens.length !== goldens.length) { + const errMsg = + `lbt-plutus-typescript: warning: expected to find ${goldens.length} golden files for ${regexFileFilter} in ${goldenDir}, but got ${foundGoldens.length}. Forgot to (re)generate the goldens? Or there is a mismatch in the number of TS goldens and generated Haskell goldens`; + console.warn(errMsg); // TODO(jaredponn): apparently there is a mismatch between the TS goldens + // and the HS goldens.. The HS script apparently only likes outputting at most 10 + // golden tests when there are clearly more tests (e.g. + // PlutusData has many more tests than just 10) + // One day, add this back in the future: + // ``` + // assert.fail(errMsg) + // ``` + // Note this doesn't actually effect the correctness of the tests. + } + + for (const [filepath, index] of foundGoldens) { + await it(`${index} at ${filepath}`, async () => { + const contents = await Fs.readFile(filepath, { encoding: "utf8" }); + assertGolden(index, contents); + }); + } +} + +/** + * @returns a function which throws an exception if + * `serialise(to(from(deserialise(contents)))) != contents` (or any of + * `serialise`, `to`, `from`, `deserialise` throws an exception) + */ +export function mkFromToAssertGolden( + deserialise: (contents: string) => A, + from: (a: A) => B, + to: (b: B) => A, + serialise: (a: A) => string, +): (index: string, contents: string) => void { + return (index: string, contents: string) => { + try { + const fromTo = serialise(to(from(deserialise(contents)))); + + if (contents !== fromTo) { + assert.fail( + `Golden test failed for ${index}. Expected:\n\`${contents}\`\nbut got\n\`${fromTo}\``, + ); + } + } catch (err) { + assert.fail( + `Golden test failed for ${index} since an error was thrown: \`${err}\`.`, + ); + } + }; +} diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/src/tsconfig.json b/testsuites/lbt-plutus/lbt-plutus-typescript/src/tsconfig.json new file mode 100644 index 00000000..fcab6f23 --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/src/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig-base", + "include": ["*"], + "references": [ ] +} diff --git a/testsuites/lbt-plutus/lbt-plutus-typescript/tsconfig-base.json b/testsuites/lbt-plutus/lbt-plutus-typescript/tsconfig-base.json new file mode 100644 index 00000000..56c4c468 --- /dev/null +++ b/testsuites/lbt-plutus/lbt-plutus-typescript/tsconfig-base.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "node16", /* Specify what module code is generated. */ + "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + /// "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/testsuites/lbt-prelude/api/build.nix b/testsuites/lbt-prelude/api/build.nix index a011e6da..89d7a890 100644 --- a/testsuites/lbt-prelude/api/build.nix +++ b/testsuites/lbt-prelude/api/build.nix @@ -20,6 +20,11 @@ _: { files = [ "Foo.lbf" "Foo/Bar.lbf" "Days.lbf" ]; }; + lbf-prelude-golden-api-typescript = config.lbf-nix.lbfPreludeTypescript { + name = "lbf-prelude-golden-api"; + src = ./.; + files = [ "Foo.lbf" "Foo/Bar.lbf" "Days.lbf" ]; + }; }; }; } diff --git a/testsuites/lbt-prelude/golden/build.nix b/testsuites/lbt-prelude/golden/build.nix index e06d95ed..21e48b96 100644 --- a/testsuites/lbt-prelude/golden/build.nix +++ b/testsuites/lbt-prelude/golden/build.nix @@ -1,6 +1,17 @@ _: { perSystem = { pkgs, config, ... }: + + let + goldenData = pkgs.stdenv.mkDerivation { + name = "lbt-prelude-golden-data"; + src = ./.; + # Disable the Fixup phase since it needs to (potentially) write to the + # files in `./.` which are readonly in the nix store + dontFixup = true; + installPhase = ''ln -s "$src" "$out"''; + }; + in { devShells.dev-lbt-prelude-golden = config.devShells.default; @@ -11,20 +22,12 @@ _: cabalPackageName = "lbt-prelude-golden-data"; }; - lbt-prelude-golden-purescript = pkgs.stdenv.mkDerivation { - name = "lbt-prelude-golden-data"; - src = ./.; - phases = "installPhase"; - installPhase = "ln -s $src $out"; - }; + lbt-prelude-golden-purescript = goldenData; - lbt-prelude-golden-rust = pkgs.stdenv.mkDerivation { - name = "lbt-prelude-golden-data"; - src = ./.; - phases = "installPhase"; - installPhase = "ln -s $src $out"; - }; - }; + lbt-prelude-golden-rust = goldenData; + + lbt-prelude-golden-typescript = goldenData; + }; }; } diff --git a/testsuites/lbt-prelude/lbt-prelude-typescript/.gitignore b/testsuites/lbt-prelude/lbt-prelude-typescript/.gitignore new file mode 100644 index 00000000..c8d23906 --- /dev/null +++ b/testsuites/lbt-prelude/lbt-prelude-typescript/.gitignore @@ -0,0 +1,4 @@ +# Ignore the autogenerated files from flake-lang.nix +.extra-dependencies +data +node_modules diff --git a/testsuites/lbt-prelude/lbt-prelude-typescript/build.nix b/testsuites/lbt-prelude/lbt-prelude-typescript/build.nix new file mode 100644 index 00000000..34ac5dd3 --- /dev/null +++ b/testsuites/lbt-prelude/lbt-prelude-typescript/build.nix @@ -0,0 +1,28 @@ +{ inputs, ... }: +{ + perSystem = { config, system, ... }: + let + tsFlake = + inputs.flake-lang.lib.${system}.typescriptFlake { + name = "lbt-prelude"; + src = ./.; + npmExtraDependencies = [ + config.packages.lbf-prelude-golden-api-typescript + ]; + + devShellTools = config.settings.shell.tools; + devShellHook = config.settings.shell.hook; + + data = + [ + { + name = "lbt-prelude-golden-data"; + path = config.packages.lbt-prelude-golden-typescript; + } + ]; + }; + in + { + inherit (tsFlake) devShells checks; + }; +} diff --git a/testsuites/lbt-prelude/lbt-prelude-typescript/package-lock.json b/testsuites/lbt-prelude/lbt-prelude-typescript/package-lock.json new file mode 100644 index 00000000..682f8d90 --- /dev/null +++ b/testsuites/lbt-prelude/lbt-prelude-typescript/package-lock.json @@ -0,0 +1,136 @@ +{ + "name": "lbf-prelude-typescript", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "lbf-prelude-typescript", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbf-prelude-golden-api": "file:.extra-dependencies/lbf-prelude-golden-api-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + }, + "devDependencies": { + "@types/node": "^20.11.7", + "typescript": "^5.3.3" + } + }, + "node_modules/@types/node": { + "version": "20.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.7.tgz", + "integrity": "sha512-GPmeN1C3XAyV5uybAf4cMLWT9fDWcmQhZVtMFu7OR32WjrqGG+Wnk2V1d0bmtUyE/Zy1QJ9BxyiTih9z8Oks8A==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/lbf-prelude": { + "version": "1.0.0", + "resolved": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "integrity": "sha512-jOL78xR3JwPMCiFZKxLFiaRvtpSlCNdWcp0NcekUwDQ5+Hg2Yg/nMqvB8lqg8orU+ZsWuHO4mo8MNPVMeyJavA==", + "dependencies": { + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "node_modules/lbf-prelude-golden-api": { + "version": "1.0.0", + "resolved": "file:.extra-dependencies/lbf-prelude-golden-api-1.0.0.tgz", + "integrity": "sha512-vXBgsy4ShhRkFNZDWnF6xu7sX8cSWxt9u0+9Z0SEPP3X80hZkwKi3rpsautdfe0SW8d/yx1T20q1SSWxYVBK9A==", + "dependencies": { + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "node_modules/lbr-prelude": { + "version": "1.0.0", + "resolved": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "integrity": "sha512-w9hPnbHU6np87kdChn7D7r021U0UUTDhKxc9rYZqTsu73bRfY1AxxUh4n4FgiF6aa+GFtHFaYd06a0Znka/PMQ==", + "license": "ISC", + "dependencies": { + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz", + "typescript": "^5.3.3" + } + }, + "node_modules/prelude": { + "version": "1.0.1", + "resolved": "file:.extra-dependencies/prelude-1.0.1.tgz", + "integrity": "sha512-NtSJIsn0+eDmzHZu3o5rK5Fh2Q3/eXmZ+tcsUsip7XmKl06RX0vTWWSsYngJhsdj+Xd7jZl/Wy92m8oNycykVA==", + "license": "ISC" + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + }, + "dependencies": { + "@types/node": { + "version": "20.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.7.tgz", + "integrity": "sha512-GPmeN1C3XAyV5uybAf4cMLWT9fDWcmQhZVtMFu7OR32WjrqGG+Wnk2V1d0bmtUyE/Zy1QJ9BxyiTih9z8Oks8A==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } + }, + "lbf-prelude": { + "version": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "integrity": "sha512-jOL78xR3JwPMCiFZKxLFiaRvtpSlCNdWcp0NcekUwDQ5+Hg2Yg/nMqvB8lqg8orU+ZsWuHO4mo8MNPVMeyJavA==", + "requires": { + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "lbf-prelude-golden-api": { + "version": "file:.extra-dependencies/lbf-prelude-golden-api-1.0.0.tgz", + "integrity": "sha512-vXBgsy4ShhRkFNZDWnF6xu7sX8cSWxt9u0+9Z0SEPP3X80hZkwKi3rpsautdfe0SW8d/yx1T20q1SSWxYVBK9A==", + "requires": { + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } + }, + "lbr-prelude": { + "version": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "integrity": "sha512-w9hPnbHU6np87kdChn7D7r021U0UUTDhKxc9rYZqTsu73bRfY1AxxUh4n4FgiF6aa+GFtHFaYd06a0Znka/PMQ==", + "requires": { + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz", + "typescript": "^5.3.3" + } + }, + "prelude": { + "version": "file:.extra-dependencies/prelude-1.0.1.tgz", + "integrity": "sha512-NtSJIsn0+eDmzHZu3o5rK5Fh2Q3/eXmZ+tcsUsip7XmKl06RX0vTWWSsYngJhsdj+Xd7jZl/Wy92m8oNycykVA==" + }, + "typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==" + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + } +} diff --git a/testsuites/lbt-prelude/lbt-prelude-typescript/package.json b/testsuites/lbt-prelude/lbt-prelude-typescript/package.json new file mode 100644 index 00000000..5c2b1d38 --- /dev/null +++ b/testsuites/lbt-prelude/lbt-prelude-typescript/package.json @@ -0,0 +1,30 @@ +{ + "name": "lbf-prelude-typescript", + "version": "1.0.0", + "description": "Test suite project for LambdaBuffers", + "type": "module", + "exports": { + ".": "./dist/index.mjs", + "./package.json": "./package.json" + }, + "scripts": { + "build": "npx tsc -b src/", + "test": "node --test" + }, + "author": "Jared Pon", + "license": "ISC", + "files": [ + "./dist/**/*", + "./.extra-dependencies/**/*" + ], + "devDependencies": { + "@types/node": "^20.11.7", + "typescript": "^5.3.3" + }, + "dependencies": { + "lbf-prelude": "file:.extra-dependencies/lbf-prelude-1.0.0.tgz", + "lbf-prelude-golden-api": "file:.extra-dependencies/lbf-prelude-golden-api-1.0.0.tgz", + "lbr-prelude": "file:.extra-dependencies/lbr-prelude-1.0.0.tgz", + "prelude": "file:.extra-dependencies/prelude-1.0.1.tgz" + } +} diff --git a/testsuites/lbt-prelude/lbt-prelude-typescript/src/Goldens.ts b/testsuites/lbt-prelude/lbt-prelude-typescript/src/Goldens.ts new file mode 100644 index 00000000..dbb13da4 --- /dev/null +++ b/testsuites/lbt-prelude/lbt-prelude-typescript/src/Goldens.ts @@ -0,0 +1,257 @@ +import * as LbrPrelude from "lbr-prelude"; +import * as PreludeSet from "prelude/Set.js"; +import * as PreludeMap from "prelude/Map.js"; +import * as Prelude from "prelude"; +import * as LbfFoo from "lbf-prelude-golden-api/LambdaBuffers/Foo.mjs"; +import * as LbfFooBar from "lbf-prelude-golden-api/LambdaBuffers/Foo/Bar.mjs"; +import * as LbfDays from "lbf-prelude-golden-api/LambdaBuffers/Days.mjs"; + +/** + * Returns some hardcoded bytes for testing. + */ +export function someBytes(): LbrPrelude.Bytes { + return Uint8Array.from([115, 111, 109, 101, 32, 98, 121, 116, 101, 115]); +} + +/** + * Returns an array of some hardcoded tests of the {@link Foo} type + */ +export function fooSumGoldens( + x: A, + y: B, + z: C, +): LbfFooBar.FooSum[] { + return [ + { name: "Foo", fields: [x, y, z] }, + { name: "Bar", fields: [x, y] }, + { name: "Baz", fields: y }, + { name: "Qax" }, + { name: "Faz", fields: 0n }, + ]; +} + +/** + * Returns a hardcoded unit test of the {@link A} type + */ +export function aGoldens(): LbfFoo.A[] { + return fooSumGoldens(1337n, false, someBytes()); +} + +/** + * Returns a hardcoded unit test of the {@link FooProd} type + */ +export function fooProdGoldens( + x: A, + y: B, + z: C, +): LbfFooBar.FooProd[] { + return [[x, y, z, 1337n]]; +} + +/** + * Returns a hardcoded unit test of some {@link B} type + */ +export function bGoldens(): LbfFoo.B[] { + return fooProdGoldens(1337n, false, someBytes()); +} + +/** + * A hardcoded unit test of some {@link FooRec} type + */ +export function fooRecGoldens( + x: A, + y: B, + z: C, +): LbfFooBar.FooRec[] { + return [ + { fooA: x, fooB: y, fooC: z, fooInt: 1337n }, + ]; +} + +/** + * A hardcoded unit test of some {@link C} type + */ +export function cGoldens(): LbfFoo.C[] { + return fooRecGoldens(1337n, false, someBytes()); +} + +/** + * A hardcoded unit test of some {@link D} type + */ +export function dGoldens(): LbfFoo.D[] { + const result: LbfFoo.D[] = []; + for (const fooSum of fooSumGoldens(1337n, false, someBytes())) { + for (const fooProd of fooProdGoldens(1337n, false, someBytes())) { + for (const fooRec of fooRecGoldens(1337n, false, someBytes())) { + result.push( + { sum: fooSum, prod: fooProd, rec: fooRec }, + ); + } + } + } + return result; +} + +/** + * A hardcoded unit test of a {@link FInt} type + */ +export function fIntGoldens(): LbfFoo.FInt[] { + return [ + { name: "Nil" }, + { name: "Rec", fields: { name: "Rec", fields: { name: "Nil" } } }, + ]; +} + +/** + * A hardcoded unit test of a {@link GInt} type + */ +export function gIntGoldens(): LbfFoo.GInt[] { + return [ + { name: "Nil" }, + { name: "Rec", fields: { name: "Rec", fields: { name: "Nil" } } }, + ]; +} + +/** + * A hardcoded unit test of some {@link Day} type + */ +export function dayGoldens(): LbfDays.Day[] { + return [ + { name: "Monday" }, + { name: "Tuesday" }, + { name: "Wednesday" }, + { name: "Thursday" }, + { name: "Friday" }, + { name: "Saturday" }, + { name: "Sunday" }, + ]; +} + +/** + * A hardcoded unit test of some {@link WorkDay} type + */ +export function workDayGoldens(): LbfDays.WorkDay[] { + return [{ name: "Monday" }, { name: "Tuesday" }, { name: "Wednesday" }, { + name: "Thursday", + }, { name: "Friday" }]; +} + +/** + * A hardcoded unit test of some {@link FreeDay} type + */ +export function freeDayGoldens(): LbfDays.FreeDay[] { + return [{ day: { name: "Saturday" } }, { day: { name: "Sunday" } }]; +} + +/** + * A hardcoded unit test of some {@link Bool} type + */ +export function boolGoldens(): LbrPrelude.Bool[] { + return [true, false]; +} + +/** + * A hardcoded unit test of some {@link Integer} type + */ +export function integerGoldens(): LbrPrelude.Integer[] { + return [ + 0n, + 1n, + -1n, + 2n ** 32n, + -(2n ** 32n), + 2n ** (64n), + -(2n ** (64n)), + 2n ** (128n), + -(2n ** (128n)), + 2n ** (256n), + -(2n ** (256n)), + ]; +} + +/** + * A hardcoded unit test of some {@link Bytes} types + */ +export function bytesGoldens(): LbrPrelude.Bytes[] { + return [Uint8Array.from([]), Uint8Array.from([0]), someBytes()]; +} + +/** + * A hardcoded unit test of some {@link Char} types + */ +export function charGoldens(): LbrPrelude.Char[] { + return ["\u{0}", "\u{A}", "\u{1f643}"] as LbrPrelude.Char[]; +} + +/** + * A hardcoded unit test of some {@link Text} types + */ +export function textGoldens(): LbrPrelude.Text[] { + return ["", "\n", "dražen popović"]; +} + +/** + * A hardcoded unit test of some {@link Maybe} types + */ +export function maybeGoldens(): LbrPrelude.Maybe[] { + return [{ name: "Nothing" }, { name: "Just", fields: true }, { + name: "Just", + fields: false, + }]; +} + +/** + * A hardcoded unit test of some {@link Either} types + */ +export function eitherGoldens(): LbrPrelude.Either< + LbrPrelude.Bool, + LbrPrelude.Text +>[] { + return [{ name: "Left", fields: true }, { name: "Left", fields: false }, { + name: "Right", + fields: "this is right", + }]; +} + +/** + * A hardcoded unit test of some {@link List} types + */ +export function listGoldens(): LbrPrelude.List[] { + return [[], [true], [false], [true, true, false, false]]; +} + +/** + * A hardcoded unit test of some {@link Set} types + */ +export function setGoldens(): LbrPrelude.Set[] { + const set1: PreludeSet.Set = new PreludeSet.Set(); + const set2: PreludeSet.Set = new PreludeSet.Set(); + PreludeSet.insert(Prelude.ordBool, true, set2); + + const set3: PreludeSet.Set = new PreludeSet.Set(); + PreludeSet.insert(Prelude.ordBool, true, set3); + PreludeSet.insert(Prelude.ordBool, false, set3); + + return [set1, set2, set3]; +} + +/** + * A hardcoded unit test of some {@link Map} types + */ +export function mapGoldens(): LbrPrelude.Map< + LbrPrelude.Bool, + LbrPrelude.Bool +>[] { + const map1: PreludeMap.Map = new PreludeMap + .Map(); + const map2: PreludeMap.Map = new PreludeMap + .Map(); + PreludeMap.insert(Prelude.ordBool, true, true, map2); + + const map3: PreludeMap.Map = new PreludeMap + .Map(); + PreludeMap.insert(Prelude.ordBool, true, true, map3); + PreludeMap.insert(Prelude.ordBool, false, false, map3); + + return [map1, map2, map3]; +} diff --git a/testsuites/lbt-prelude/lbt-prelude-typescript/src/Json-test.ts b/testsuites/lbt-prelude/lbt-prelude-typescript/src/Json-test.ts new file mode 100644 index 00000000..b9353ea4 --- /dev/null +++ b/testsuites/lbt-prelude/lbt-prelude-typescript/src/Json-test.ts @@ -0,0 +1,260 @@ +// Unit tests for functionality in `src/Prelude/Runtime/Json.ts` +import { describe, it } from "node:test"; +import * as assert from "node:assert/strict"; +import * as Goldens from "./Goldens.js"; +import * as Fs from "node:fs/promises"; +import * as Path from "node:path"; +import * as Prelude from "prelude"; +import * as PreludeJson from "prelude/Json.js"; + +import * as LbfFoo from "lbf-prelude-golden-api/LambdaBuffers/Foo.mjs"; +import * as LbfDays from "lbf-prelude-golden-api/LambdaBuffers/Days.mjs"; + +import * as LbrPrelude from "lbr-prelude"; + +export async function findGoldens( + goldenDir: string, + title: string, +): Promise<[string, string][]> { + const files: string[] = await Fs.readdir(goldenDir, { recursive: true }); + + const filteredFiles: [string, string][] = []; + for (const file of files) { + if ( + // `title` is a prefix of `file`'s basename + Path.basename(file).startsWith(title) && + // AND it ends with `.json` + Path.extname(file) === ".json" + ) { + filteredFiles.push( + [Path.join(goldenDir, file), Path.basename(file, ".json")], + ); + } + } + return filteredFiles; +} + +export async function fromToGoldenTest( + jsonDict: Prelude.Json, + goldenDir: string, + title: string, + goldens: A[], +): Promise { + const foundGoldens: [string, string][] = await findGoldens(goldenDir, title); + + if (foundGoldens.length !== goldens.length) { + assert.fail( + `Expected to find ${goldens.length} .json golden files for ${title}, but got ${foundGoldens.length}. Forgot to (re)generate the goldens?`, + ); + } + + for (const [filepath, index] of foundGoldens) { + await it(`${index} at ${filepath}`, async () => { + const contents = await Fs.readFile(filepath, { encoding: "utf8" }); + + try { + const fromToJson = PreludeJson.stringify( + jsonDict.toJson( + jsonDict.fromJson( + PreludeJson.parseJson(contents), + ), + ), + ); + + if (contents !== fromToJson) { + assert.fail( + `Golden test failed for ${index}. Expected:\n\`${contents}\`\nbut got\n\`${fromToJson}\``, + ); + } + } catch (err) { + assert.fail( + `Golden test failed for ${index} since an error was thrown: \`${err}\`.`, + ); + } + }); + } +} + +describe("JSON tests (toJson . fromJson)", () => { + const goldenDir = `data/lbt-prelude-golden-data`; + it(`Foo.A from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbfFoo.A], + goldenDir, + `Foo.A`, + Goldens.aGoldens(), + ); + }); + + it(`Foo.B from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbfFoo.B], + goldenDir, + `Foo.B`, + Goldens.bGoldens(), + ); + }); + + it(`Foo.C from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbfFoo.C], + goldenDir, + `Foo.C`, + Goldens.cGoldens(), + ); + }); + + it(`Foo.D from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbfFoo.D], + goldenDir, + `Foo.D`, + Goldens.dGoldens(), + ); + }); + + it(`Foo.FInt from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbfFoo.FInt], + goldenDir, + `Foo.FInt`, + Goldens.fIntGoldens(), + ); + }); + + it(`Foo.GInt from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbfFoo.GInt], + goldenDir, + `Foo.GInt`, + Goldens.gIntGoldens(), + ); + }); + + it(`Days.Day from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbfDays.Day], + goldenDir, + `Days.Day`, + Goldens.dayGoldens(), + ); + }); + + it(`Days.WorkDay from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbfDays.WorkDay], + goldenDir, + `Days.WorkDay`, + Goldens.workDayGoldens(), + ); + }); + + it(`Days.FreeDay from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbfDays.FreeDay], + goldenDir, + `Days.FreeDay`, + Goldens.freeDayGoldens(), + ); + }); + + it(`Prelude.Bool from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.Bool], + goldenDir, + `Prelude.Bool`, + Goldens.boolGoldens(), + ); + }); + + it(`Prelude.Char from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.Char], + goldenDir, + `Prelude.Char`, + Goldens.charGoldens(), + ); + }); + + it(`Prelude.Integer from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.Integer], + goldenDir, + `Prelude.Integer`, + Goldens.integerGoldens(), + ); + }); + + it(`Prelude.Text from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.Text], + goldenDir, + `Prelude.Text`, + Goldens.textGoldens(), + ); + }); + + it(`Prelude.Bytes from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.Bytes], + goldenDir, + `Prelude.Bytes`, + Goldens.bytesGoldens(), + ); + }); + + it(`Prelude.Maybe from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.Maybe](LbrPrelude.Json[LbrPrelude.Bool]), + goldenDir, + `Prelude.Maybe`, + Goldens.maybeGoldens(), + ); + }); + + it(`Prelude.Either from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.Either]( + LbrPrelude.Json[LbrPrelude.Bool], + LbrPrelude.Json[LbrPrelude.Text], + ), + goldenDir, + `Prelude.Either`, + Goldens.eitherGoldens(), + ); + }); + + it(`Prelude.List from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.List](LbrPrelude.Json[LbrPrelude.Bool]), + goldenDir, + `Prelude.List`, + Goldens.listGoldens(), + ); + }); + + it(`Prelude.Set from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.Set]( + Prelude.ordBool, + LbrPrelude.Json[LbrPrelude.Bool], + ), + goldenDir, + `Prelude.Set`, + Goldens.setGoldens(), + ); + }); + + it(`Prelude.Map from to golden tests`, async () => { + await fromToGoldenTest( + LbrPrelude.Json[LbrPrelude.Map]( + Prelude.ordBool, + LbrPrelude.Json[LbrPrelude.Bool], + LbrPrelude.Json[LbrPrelude.Bool], + ), + goldenDir, + `Prelude.Map`, + Goldens.mapGoldens(), + ); + }); +}); diff --git a/testsuites/lbt-prelude/lbt-prelude-typescript/src/tsconfig.json b/testsuites/lbt-prelude/lbt-prelude-typescript/src/tsconfig.json new file mode 100644 index 00000000..fcab6f23 --- /dev/null +++ b/testsuites/lbt-prelude/lbt-prelude-typescript/src/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig-base", + "include": ["*"], + "references": [ ] +} diff --git a/testsuites/lbt-prelude/lbt-prelude-typescript/tsconfig-base.json b/testsuites/lbt-prelude/lbt-prelude-typescript/tsconfig-base.json new file mode 100644 index 00000000..56c4c468 --- /dev/null +++ b/testsuites/lbt-prelude/lbt-prelude-typescript/tsconfig-base.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "node16", /* Specify what module code is generated. */ + "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + /// "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}