Skip to content

Commit

Permalink
fix: map byte[] to bytes (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-makerx authored May 18, 2023
1 parent 5d901c9 commit 68bdee3
Show file tree
Hide file tree
Showing 24 changed files with 1,430 additions and 33 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-python.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
set -o pipefail
pipx install algokit
algokit localnet start
sleep 5 # wait for before running tests
poetry run pytest --junitxml=pytest-junit.xml --cov-report=term-missing:skip-covered --cov=src | tee pytest-coverage.txt
algokit localnet stop
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Pull Request validation

on: [pull_request]

jobs:
pr-check:
name: Check Python
uses: ./.github/workflows/check-python.yaml

pr-build:
name: Build and Test Python
needs: pr-check
uses: ./.github/workflows/build-python.yaml
Empty file added examples/__init__.py
Empty file.
43 changes: 43 additions & 0 deletions examples/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import imp
import logging
import os
import sys
from collections.abc import Generator
from contextlib import contextmanager
from pathlib import Path

logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)-10s: %(message)s")
logger = logging.getLogger(__name__)
root_path = Path(__file__).parent


@contextmanager
def cwd(path: Path) -> Generator[None, None, None]:
old_pwd = os.getcwd()
os.chdir(path)
try:
yield
finally:
os.chdir(old_pwd)


def main(action: str) -> None:
match action:
case "build":
example_dirs = filter(lambda file: file.is_dir() and "__" not in file.name, root_path.glob("*"))
for example in example_dirs:
logger.info(f"Building example {example.name}")
with cwd(root_path):
evaluated_file = imp.load_source(example.name, f"./{example.name}/{example.name}.py")
app = evaluated_file.app
logger.info(f" Building app {app.name}")
appspec = app.build()
logger.info(f" Writing {example.name}/application.json")
(example / "application.json").write_text(appspec.to_json())


if __name__ == "__main__":
if len(sys.argv) > 1:
main(sys.argv[1])
else:
main("build")
11 changes: 11 additions & 0 deletions examples/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import logging

from .helloworld import helloworld
from .lifecycle import lifecycle
from .state import state
from .voting import voting

logger = logging.getLogger(__name__)

# define example contracts to build
contracts = [helloworld.app, lifecycle.app, state.app, voting.app]
22 changes: 22 additions & 0 deletions examples/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import uuid

import algokit_utils
import pytest
from algosdk.v2client.algod import AlgodClient
from algosdk.v2client.indexer import IndexerClient


@pytest.fixture(scope="session")
def algod_client() -> AlgodClient:
return algokit_utils.get_algod_client(algokit_utils.get_default_localnet_config("algod"))


@pytest.fixture(scope="session")
def indexer_client() -> IndexerClient:
return algokit_utils.get_indexer_client(algokit_utils.get_default_localnet_config("indexer"))


@pytest.fixture()
def new_account(algod_client: AlgodClient) -> algokit_utils.Account:
unique_name = str(uuid.uuid4()).replace("-", "")
return algokit_utils.get_account(algod_client, unique_name)
21 changes: 21 additions & 0 deletions examples/deployment_standard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import beaker
import pyteal as pt
from algokit_utils import DELETABLE_TEMPLATE_NAME, UPDATABLE_TEMPLATE_NAME


def deploy_time_immutability_control(app: beaker.Application) -> None:
@app.update(authorize=beaker.Authorize.only_creator(), bare=True)
def update() -> pt.Expr:
return pt.Assert(
pt.Tmpl.Int(UPDATABLE_TEMPLATE_NAME),
comment="Check app is updatable",
)


def deploy_time_permanence_control(app: beaker.Application) -> None:
@app.delete(authorize=beaker.Authorize.only_creator(), bare=True)
def delete() -> pt.Expr:
return pt.Assert(
pt.Tmpl.Int(DELETABLE_TEMPLATE_NAME),
comment="Check app is deletable",
)
3 changes: 3 additions & 0 deletions examples/helloworld/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from algokit_client_generator.writer import generate_client

__all__ = ["generate_client"]
21 changes: 21 additions & 0 deletions examples/helloworld/helloworld.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import beaker
import pyteal as pt

from examples.deployment_standard import (
deploy_time_immutability_control,
deploy_time_permanence_control,
)

app = beaker.Application("HelloWorldApp").apply(deploy_time_immutability_control).apply(deploy_time_permanence_control)


@app.external
def hello(name: pt.abi.String, *, output: pt.abi.String) -> pt.Expr:
"""Returns Hello, {name}"""
return output.set(pt.Concat(pt.Bytes("Hello, "), name.get()))


@app.external
def hello_world_check(name: pt.abi.String) -> pt.Expr:
"""Asserts {name} is "World" """
return pt.Assert(name.get() == pt.Bytes("World"))
48 changes: 48 additions & 0 deletions examples/helloworld/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import pytest
from algokit_utils import OnUpdate, get_localnet_default_account
from algosdk.atomic_transaction_composer import AccountTransactionSigner
from algosdk.v2client.algod import AlgodClient
from algosdk.v2client.indexer import IndexerClient

from examples.helloworld.client_generated import HelloWorldAppClient


@pytest.fixture(scope="session")
def helloworld_client(algod_client: AlgodClient, indexer_client: IndexerClient) -> HelloWorldAppClient:
client = HelloWorldAppClient(
algod_client=algod_client,
indexer_client=indexer_client,
creator=get_localnet_default_account(algod_client),
)
client.deploy(allow_delete=True, allow_update=True, on_update=OnUpdate.UpdateApp)
return client


def test_hello(helloworld_client: HelloWorldAppClient) -> None:
response = helloworld_client.hello(name="World")

assert response.return_value == "Hello, World"


def test_hello_check_args(helloworld_client: HelloWorldAppClient) -> None:
response = helloworld_client.hello_world_check(name="World")

assert response.return_value is None


def test_lifecycle(algod_client: AlgodClient) -> None:
account = get_localnet_default_account(algod_client)
signer = AccountTransactionSigner(account.private_key)

helloworld_client = HelloWorldAppClient(
algod_client=algod_client, signer=signer, template_values={"UPDATABLE": 1, "DELETABLE": 1}
)

assert helloworld_client.create()
assert helloworld_client.update()

response = helloworld_client.hello(name="World")

assert response.return_value == "Hello, World"

assert helloworld_client.delete()
3 changes: 3 additions & 0 deletions examples/lifecycle/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from algokit_client_generator.writer import generate_client

__all__ = ["generate_client"]
76 changes: 76 additions & 0 deletions examples/lifecycle/lifecycle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import dataclasses
from typing import cast

import beaker
import pyteal as pt
from beaker.lib.iter import Iterate
from beaker.lib.strings import Itoa
from pyteal.ast import CallConfig, MethodConfig

from examples.deployment_standard import (
deploy_time_immutability_control,
)


@dataclasses.dataclass
class LifeCycleData:
greeting = beaker.GlobalStateValue(stack_type=pt.TealType.bytes)
times = beaker.GlobalStateValue(stack_type=pt.TealType.uint64)


app = beaker.Application("LifeCycleApp", state=LifeCycleData()).apply(deploy_time_immutability_control)


@app.external
def hello(name: pt.abi.String, *, output: pt.abi.String) -> pt.Expr:
return pt.Seq(
(buff := pt.ScratchVar()).store(pt.Bytes("")),
Iterate(
buff.store(
pt.Concat(buff.load(), app.state.greeting.get(), pt.Bytes(", "), name.get(), pt.Bytes("\n"))
), # result += greeting, name\n
cast(pt.Int, app.state.times.get()),
),
output.set(buff.load()),
)


@app.external(name="hello")
def hello_no_arg(*, output: pt.abi.String) -> pt.Expr:
return pt.Seq(
(buff := pt.ScratchVar()).store(pt.Bytes("")),
Iterate(
buff.store(
pt.Concat(buff.load(), app.state.greeting.get(), pt.Bytes(", mystery person\n"))
), # result += greeting, mystery person\n
cast(pt.Int, app.state.times.get()),
),
output.set(buff.load()),
)


@app.external(bare=True, method_config=MethodConfig(no_op=CallConfig.CREATE, opt_in=CallConfig.CREATE))
def bare_create() -> pt.Expr:
"""Bare create method"""
return pt.Seq(app.state.greeting.set(pt.Bytes("Hello")), app.state.times.set(pt.Int(1)), pt.Approve())


@app.create(name="create")
def create_1arg(greeting: pt.abi.String, *, output: pt.abi.String) -> pt.Expr:
"""ABI create method with 1 argument"""
return pt.Seq(
app.state.greeting.set(greeting.get()),
app.state.times.set(pt.Int(1)),
output.set(pt.Concat(greeting.get(), pt.Bytes("_"), Itoa(app.state.times.get()))),
)


@app.create(name="create")
def create_2arg(greeting: pt.abi.String, times: pt.abi.Uint32) -> pt.Expr:
"""ABI create method with 2 arguments"""
return pt.Seq(app.state.greeting.set(greeting.get()), app.state.times.set(times.get()), pt.Approve())


@app.clear_state()
def clear() -> pt.Expr:
return pt.Approve()
94 changes: 94 additions & 0 deletions examples/lifecycle/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import pytest
from algokit_utils import (
Account,
get_localnet_default_account,
)
from algosdk.atomic_transaction_composer import AccountTransactionSigner
from algosdk.v2client.algod import AlgodClient
from algosdk.v2client.indexer import IndexerClient

from examples.lifecycle.client_generated import (
CreateStringArgs,
CreateVoidArgs,
DeployCreate_CreateStringArgs,
DeployCreate_CreateVoidArgs,
LifeCycleAppClient,
)


@pytest.fixture(scope="session")
def lifecycle_client(algod_client: AlgodClient, indexer_client: IndexerClient) -> LifeCycleAppClient:
account = get_localnet_default_account(algod_client)
signer = AccountTransactionSigner(account.private_key)

return LifeCycleAppClient(algod_client=algod_client, signer=signer, template_values={"UPDATABLE": 1})


def test_create_bare(lifecycle_client: LifeCycleAppClient) -> None:
create_response = lifecycle_client.create(args=None)
assert create_response
response = lifecycle_client.hello_1_args(name="Bare")

assert response.return_value == "Hello, Bare\n"


def test_create_1arg(lifecycle_client: LifeCycleAppClient) -> None:
create_response = lifecycle_client.create(args=CreateStringArgs(greeting="Greetings"))
assert create_response.return_value == "Greetings_1"
response = lifecycle_client.hello_1_args(name="1 Arg")

assert response.return_value == "Greetings, 1 Arg\n"


def test_create_2arg(lifecycle_client: LifeCycleAppClient) -> None:
create_response = lifecycle_client.create(args=CreateVoidArgs(greeting="Greetings", times=2))
assert create_response.return_value is None
response = lifecycle_client.hello_1_args(name="2 Arg")

assert response.return_value == "Greetings, 2 Arg\nGreetings, 2 Arg\n"


@pytest.fixture()
def deploy_lifecycle_client(
algod_client: AlgodClient, indexer_client: IndexerClient, new_account: Account
) -> LifeCycleAppClient:
return LifeCycleAppClient(
algod_client=algod_client,
indexer_client=indexer_client,
creator=new_account,
)


def test_deploy_bare(deploy_lifecycle_client: LifeCycleAppClient) -> None:
deploy_lifecycle_client.deploy(allow_update=True, create_args=None)
assert deploy_lifecycle_client.app_client.app_id

response = deploy_lifecycle_client.hello_1_args(name="Deploy Bare")

assert response.return_value == "Hello, Deploy Bare\n"


def test_deploy_create_1arg(deploy_lifecycle_client: LifeCycleAppClient) -> None:
deploy_lifecycle_client.deploy(
allow_update=True,
create_args=DeployCreate_CreateStringArgs(args=CreateStringArgs(greeting="Deploy Greetings")),
)
assert deploy_lifecycle_client.app_client.app_id

response = deploy_lifecycle_client.hello_1_args(name="1 Arg")

assert response.return_value == "Deploy Greetings, 1 Arg\n"


def test_deploy_create_2arg(deploy_lifecycle_client: LifeCycleAppClient) -> None:
deploy_lifecycle_client.deploy(
allow_update=True,
create_args=DeployCreate_CreateVoidArgs(
args=CreateVoidArgs(greeting="Deploy Greetings", times=2),
),
)
assert deploy_lifecycle_client.app_client.app_id

response = deploy_lifecycle_client.hello_1_args(name="2 Arg")

assert response.return_value == "Deploy Greetings, 2 Arg\nDeploy Greetings, 2 Arg\n"
3 changes: 3 additions & 0 deletions examples/state/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from algokit_client_generator.writer import generate_client

__all__ = ["generate_client"]
Loading

1 comment on commit 68bdee3

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/algokit_client_generator
   __main__.py220%1–3
   cli.py36360%1–46
   document.py81791%43, 46, 48–49, 59, 73–74
   generator.py341698%102, 105, 201, 218, 382, 415
   spec.py94298%109, 118
   utils.py90693%15, 22, 37, 60, 100, 105
   writer.py15193%14
TOTAL6616091% 

Tests Skipped Failures Errors Time
28 0 💤 0 ❌ 0 🔥 1m 13s ⏱️

Please sign in to comment.