diff --git a/.github/workflows/spawn_examples.yml b/.github/workflows/spawn_examples.yml deleted file mode 100644 index a8c6a179c..000000000 --- a/.github/workflows/spawn_examples.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Spawn Examples -on: - workflow_dispatch: - pull_request: - push: - branches: - - main -jobs: - generate_example_paths: - name: Generate example paths - runs-on: ubuntu-latest - outputs: - example_paths: ${{ steps.generate_example_paths.outputs.example_paths }} - steps: - - uses: actions/checkout@v3 - - name: Setup fd-find - uses: ./.github/actions/setup-fd-find - - id: generate_example_paths - run: | - fd --print0 --glob "*.ts" ./examples | jq -Rnc '(input | split("\u0000"))' > example_paths.json - example_paths=`cat example_paths.json` - echo "example_paths=$example_paths" >> $GITHUB_OUTPUT - run_example_workflow: - needs: generate_example_paths - name: Run examples - uses: ./.github/workflows/example.yml - with: - example_paths: ${{ needs.generate_example_paths.outputs.example_paths }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index da8904bc3..e12ea9d9b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,3 +19,4 @@ jobs: - run: deno task codegen - run: deno task star - run: deno task test + - run: deno task test:examples diff --git a/_tasks/test_examples.ts b/_tasks/test_examples.ts new file mode 100644 index 000000000..ed5fb6922 --- /dev/null +++ b/_tasks/test_examples.ts @@ -0,0 +1,66 @@ +import { Buffer, readLines } from "../deps/std/io.ts" +import * as path from "../deps/std/path.ts" +import { writeAll } from "../deps/std/streams.ts" +import { assert } from "../deps/std/testing/asserts.ts" + +const examplesDir = Deno.args[0] +if (!examplesDir) { + throw new Error("specify examples directory as first CLI argument") +} + +const ignoreFile = await Deno.readTextFile(path.join(examplesDir, ".ignore")) +const ignoredFiles = new Set(ignoreFile.split("\n")) + +const exampleFileNames = Array.from(Deno.readDirSync(examplesDir)) + .filter((e) => e.name.match(/^.*\.ts$/g) && e.isFile && !ignoredFiles.has(e.name)) + .map((f) => f.name) + +Deno.test("examples", async (t) => { + await Promise.all(exampleFileNames.map((fileName) => { + return t.step({ + name: fileName, + async fn() { + const task = Deno.run({ + cmd: ["deno", "task", "run", `${examplesDir}/${fileName}`], + stdout: "piped", + stderr: "piped", + }) + + try { + const out = new Buffer() + + await Promise.all([ + pipeThrough(task.stdout, out), + pipeThrough(task.stderr, out), + ]) + + const status = await task.status() + + if (!status.success) { + for await (const line of readLines(out)) { + console.log(line) + } + } + assert(status.success, `task failed with status code: ${status.code}`) + } finally { + task.stdout.close() + task.stderr.close() + task.close() + } + }, + sanitizeExit: false, + sanitizeOps: false, + sanitizeResources: false, + }) + })) +}) + +async function pipeThrough( + reader: Deno.Reader, + writer: Deno.Writer, +) { + const encoder = new TextEncoder() + for await (const line of readLines(reader)) { + await writeAll(writer, encoder.encode(`${line}\n`)) + } +} diff --git a/deno.jsonc b/deno.jsonc index fbff8f1b8..73deb5361 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -36,6 +36,7 @@ "star": "deno task run _tasks/star.ts && deno cache --check target/star.ts", "codegen": "deno task run cache.ts examples/mod.ts", "test": "deno task run test_util/ctx.ts -- deno test -A -L=info --ignore=target --parallel", + "test:examples": "deno task test _tasks/test_examples.ts -- examples", "test:update": "deno task test -- -- --update", "bench": "deno bench -A", "moderate": "deno task run https://deno.land/x/moderate@0.0.5/mod.ts && dprint fmt" diff --git a/deps/std/io.ts b/deps/std/io.ts new file mode 100644 index 000000000..0328cb286 --- /dev/null +++ b/deps/std/io.ts @@ -0,0 +1 @@ +export * from "https://deno.land/std@0.172.0/io/mod.ts" diff --git a/deps/std/streams.ts b/deps/std/streams.ts new file mode 100644 index 000000000..6f625b942 --- /dev/null +++ b/deps/std/streams.ts @@ -0,0 +1 @@ +export * from "https://deno.land/std@0.172.0/streams/mod.ts" diff --git a/effects/extrinsic.test.ts b/effects/extrinsic.test.ts index 05dca1d3d..c11053b03 100644 --- a/effects/extrinsic.test.ts +++ b/effects/extrinsic.test.ts @@ -1,12 +1,17 @@ import * as A from "../deps/std/testing/asserts.ts" import * as T from "../test_util/mod.ts" import * as U from "../util/mod.ts" +import { Sr25519 } from "../util/mod.ts" import { entryRead } from "./entryRead.ts" import { extrinsic } from "./extrinsic.ts" +const EXISTENTIAL_DEPOSIT_AMOUNT = 10_000_000_000n + Deno.test({ name: "Balances.transfer", fn: async (ctx) => { + const kp = Sr25519.fromSeed(crypto.getRandomValues(new Uint8Array(32))) + await ctx.step("extrinsic events", async () => { await assertExtrinsicStatusOrder({ keypair: T.alice, @@ -14,10 +19,10 @@ Deno.test({ type: "Balances", value: { type: "transfer", - value: 12345n, + value: EXISTENTIAL_DEPOSIT_AMOUNT + 12345n, dest: { type: "Id", - value: T.bob.publicKey, + value: kp.publicKey, }, }, }, @@ -28,10 +33,10 @@ Deno.test({ await ctx.step({ name: "account balance updated", fn: async () => { - const state = await entryRead(T.westend)("System", "Account", [T.bob.publicKey]).run() + const state = await entryRead(T.westend)("System", "Account", [kp.publicKey]).run() A.assertObjectMatch(state, { value: { - data: { free: 10000000000012345n }, + data: { free: EXISTENTIAL_DEPOSIT_AMOUNT + 12345n }, }, }) }, diff --git a/examples/batch.ts b/examples/batch.ts index dd0aeab4c..1b3eec820 100644 --- a/examples/batch.ts +++ b/examples/batch.ts @@ -1,4 +1,3 @@ -import * as C from "http://localhost:5646/@local/mod.ts" import * as T from "http://localhost:5646/@local/test_util/mod.ts" import * as U from "http://localhost:5646/@local/util/mod.ts" @@ -7,6 +6,7 @@ import { Balances, Utility, } from "http://localhost:5646/@local/proxy/dev:westend/@v0.9.36/pallets/mod.ts" +import { collectExtrinsicEvents } from "http://localhost:5646/@local/test_util/extrinsic.ts" // TODO: uncomment these lines / use env upon solving `count` in zones // const getBalances = C.Z.ls( @@ -16,24 +16,18 @@ import { // }), // ) -const tx = extrinsic({ - sender: T.alice.address, - call: Utility.batchAll({ - calls: T.users.map((pair) => - Balances.transfer({ - dest: pair.address, - value: 12345n, - }) - ), - }), -}) - .signed(T.alice.sign) - .watch((ctx) => (status) => { - console.log(status) - if (C.rpc.known.TransactionStatus.isTerminal(status)) { - return ctx.end() - } - return - }) +const root = collectExtrinsicEvents( + extrinsic({ + sender: T.dave.address, + call: Utility.batchAll({ + calls: T.users.map((pair) => + Balances.transfer({ + dest: pair.address, + value: 12345n, + }) + ), + }), + }).signed(T.dave.sign), +).next(console.log) -U.throwIfError(await tx.run()) +U.throwIfError(await root.run()) diff --git a/examples/polkadot_js_signer.ts b/examples/polkadot_js_signer.ts index 0b76dcd39..e6beda844 100644 --- a/examples/polkadot_js_signer.ts +++ b/examples/polkadot_js_signer.ts @@ -1,38 +1,34 @@ -import { createTestPairs } from "https://deno.land/x/polkadot@0.0.8/keyring/mod.ts" -import { TypeRegistry } from "https://deno.land/x/polkadot@0.0.8/types/mod.ts" - import * as C from "http://localhost:5646/@local/mod.ts" +import { collectExtrinsicEvents } from "http://localhost:5646/@local/test_util/extrinsic.ts" + import * as T from "http://localhost:5646/@local/test_util/mod.ts" import * as U from "http://localhost:5646/@local/util/mod.ts" +import { createTestPairs } from "https://deno.land/x/polkadot@0.0.8/keyring/mod.ts" +import { TypeRegistry } from "https://deno.land/x/polkadot@0.0.8/types/mod.ts" -const root = C.extrinsic(T.westend)({ - sender: T.alice.address, - call: { - type: "Balances", - value: { - type: "transfer", - value: 12345n, - dest: T.bob.address, +const root = collectExtrinsicEvents( + C.extrinsic(T.westend)({ + sender: T.dave.address, + call: { + type: "Balances", + value: { + type: "transfer", + value: 12345n, + dest: T.bob.address, + }, }, - }, -}) - .signed({ - signPayload(payload) { - const tr = new TypeRegistry() - tr.setSignedExtensions(payload.signedExtensions) - return Promise.resolve( - tr - .createType("ExtrinsicPayload", payload, { version: payload.version }) - .sign(createTestPairs().alice!), - ) - }, - }) - .watch((ctx) => (status) => { - console.log(status) - if (C.rpc.known.TransactionStatus.isTerminal(status)) { - return ctx.end() - } - return }) + .signed({ + signPayload(payload) { + const tr = new TypeRegistry() + tr.setSignedExtensions(payload.signedExtensions) + return Promise.resolve( + tr + .createType("ExtrinsicPayload", payload, { version: payload.version }) + .sign(createTestPairs().dave!), + ) + }, + }), +).next(console.log) U.throwIfError(await root.run())