diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1605611422..38a1bc1759 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: needs: build-test-forge-e2e-nextest-archive strategy: matrix: - partition: [ 1, 2, 3, 4, 5, 6, 7, 8 ] + partition: [1, 2, 3, 4, 5, 6, 7, 8] steps: - name: Extract branch name if: github.event_name != 'pull_request' @@ -88,8 +88,8 @@ jobs: - name: nextest partition ${{ matrix.partition }}/8 run: cargo nextest run --partition 'count:${{ matrix.partition }}/8' --archive-file 'nextest-archive.tar.zst' e2e - test-coverage: - name: Test coverage + test-scarb-2-8-3: + name: Test scarb 2.8.3 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -105,6 +105,7 @@ jobs: curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh - run: cargo test --package forge --features scarb_2_8_3 --test main e2e::coverage + - run: cargo test --package forge --features scarb_2_8_3 --test main e2e::backtrace test-coverage-error: name: Test coverage error @@ -141,6 +142,8 @@ jobs: cargo test --package forge --features scarb_2_8_3 e2e::features - run: | cargo test --package scarb-api --features scarb_2_8_3 get_starknet_artifacts_path + - run: | + cargo test --package scarb-api --features scarb_2_8_3 test_load_contracts_artifacts test-forge-runner: name: Test Forge Runner diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 887b79de6a..9deb4d0e58 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -37,6 +37,10 @@ jobs: toolchain: stable - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + - name: Install sitemap CLI + run: | + npm i -g static-sitemap-cli - name: Install mdBook run: | cargo install --version ${MDBOOK_VERSION} mdbook @@ -78,6 +82,10 @@ jobs: curl -o highlight.js https://raw.githubusercontent.com/software-mansion/scarb/main/extensions/scarb-doc/theme/highlight.js cp highlight.js ./docs/book/html/sncast_std/highlight.js cp highlight.js ./docs/book/html/snforge_std/highlight.js + - name: Generate sitemap + run: | + sscli --base https://foundry-rs.github.io/starknet-foundry + working-directory: ./docs/book/html - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: diff --git a/.github/workflows/publish_plugin.yml b/.github/workflows/publish_plugin.yml new file mode 100644 index 0000000000..bc8ac8c364 --- /dev/null +++ b/.github/workflows/publish_plugin.yml @@ -0,0 +1,37 @@ +name: Publish snforge_scarb_plugin + +on: + workflow_call: + workflow_dispatch: + +jobs: + upload-to-registry: + name: Upload plugin to the registry + runs-on: ubuntu-latest + env: + SCARB_REGISTRY_AUTH_TOKEN: ${{ secrets.SCARB_REGISTRY_AUTH_TOKEN }} + steps: + - uses: actions/checkout@v4 + + - name: Check version + id: check-version + if: ${{ github.event_name != 'workflow_dispatch' }} + run: | + set -exo pipefail + + snforge_scarb_plugin_version=$(grep version crates/snforge-scarb-plugin/Scarb.toml | cut -d '"' -f 2) + snforge_scarb_plugin_uploaded=$(curl -s https://scarbs.xyz/api/v1/index/sn/fo/snforge_scarb_plugin.json | jq --arg version "$snforge_scarb_plugin_version" '[.[] | select(.v == $version)] | length > 0') + echo "snforge_scarb_plugin_uploaded=$snforge_scarb_plugin_uploaded" >> $GITHUB_OUTPUT + + - uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336a9b35a + with: + toolchain: stable + + - uses: software-mansion/setup-scarb@v1 + with: + scarb-version: "2.8.5" + + - name: Publish snforge_scarb_plugin + if: ${{ steps.check-version.outputs.snforge_scarb_plugin_uploaded == 'false' || github.event_name == 'workflow_dispatch' }} + working-directory: crates/snforge-scarb-plugin + run: scarb publish diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6ee84895f9..1ccf3a013d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -249,3 +249,35 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG: ${{ steps.create-release.outputs.computed-prefix }}${{ steps.create-release.outputs.version }} + + publish-snforge-scarb-plugin: + name: Publish snforge_scarb_plugin + uses: ./.github/workflows/publish_plugin.yml + secrets: inherit + + publish-to-registry: + name: Publish packages to the registry + runs-on: ubuntu-latest + needs: [ create-release, publish-snforge-scarb-plugin ] + env: + SCARB_REGISTRY_AUTH_TOKEN: ${{ secrets.SCARB_REGISTRY_AUTH_TOKEN }} + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336a9b35a + with: + toolchain: stable + + - uses: software-mansion/setup-scarb@v1 + with: + scarb-version: "2.8.5" + + - name: Publish sncast_std + working-directory: sncast_std + run: scarb publish --allow-dirty + + - name: Publish snforge_std + working-directory: snforge_std + run: | + ../scripts/set_plugin_version.sh + scarb publish --allow-dirty diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml new file mode 100644 index 0000000000..08d1eb1ea8 --- /dev/null +++ b/.github/workflows/scheduled.yml @@ -0,0 +1,125 @@ +name: Scheduled + +on: + pull_request: + paths: + - scripts/get_scarb_versions.sh + - .github/workflows/scheduled.yml + schedule: + - cron: '0 0 * * 3,0' + +jobs: + get-scarb-versions: + name: Get Scarb versions + outputs: + versions: ${{ steps.get_versions.outputs.versions }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: asdf-vm/actions/install@05e0d2ed97b598bfce82fd30daf324ae0c4570e6 + with: + tool_versions: | + scarb latest + + - name: Get versions + id: get_versions + run: | + scarb_versions=$(./scripts/get_scarb_versions.sh) + echo ${scarb_versions[@]} + echo "versions=[${scarb_versions[@]}]" >> "$GITHUB_OUTPUT" + + test-forge-unit-and-integration: + runs-on: ubuntu-latest + needs: get-scarb-versions + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.get-scarb-versions.outputs.versions) }} + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab + - uses: software-mansion/setup-scarb@v1 + with: + scarb-version: ${{ matrix.version }} + - uses: software-mansion/setup-universal-sierra-compiler@v1 + + - run: cargo test --release --lib -p forge + - run: cargo test --release -p forge integration + + test-forge-e2e: + runs-on: ubuntu-latest + needs: get-scarb-versions + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.get-scarb-versions.outputs.versions) }} + + steps: + - name: Extract branch name + if: github.event_name != 'pull_request' + run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV + + - name: Extract branch name on pull request + if: github.event_name == 'pull_request' + run: echo "BRANCH_NAME=$(echo $GITHUB_HEAD_REF)" >> $GITHUB_ENV + + - name: Extract repo name and owner + if: github.event_name != 'pull_request' + run: echo "REPO_NAME=$(echo ${{ github.repository }}.git)" >> $GITHUB_ENV + + - name: Extract repo name and owner on pull request + if: github.event_name == 'pull_request' + run: echo "REPO_NAME=$(echo ${{ github.event.pull_request.head.repo.full_name }}.git)" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab + - uses: software-mansion/setup-scarb@v1 + with: + scarb-version: ${{ matrix.version }} + - uses: software-mansion/setup-universal-sierra-compiler@v1 + - name: Install cairo-profiler + run: | + curl -L https://raw.githubusercontent.com/software-mansion/cairo-profiler/main/scripts/install.sh | sh + - uses: taiki-e/install-action@nextest + + - run: cargo test --release -p forge e2e + + test-cast: + runs-on: ubuntu-latest + needs: get-scarb-versions + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.get-scarb-versions.outputs.versions) }} + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab + - uses: software-mansion/setup-scarb@v1 + with: + scarb-version: ${{ matrix.version }} + - uses: software-mansion/setup-universal-sierra-compiler@v1 + + - name: Install starknet-devnet-rs + run: ./scripts/install_devnet.sh + + - run: cargo test --release -p sncast + + notify_if_failed: + runs-on: ubuntu-latest + if: always() && contains(needs.*.result, 'failure') && github.event_name == 'schedule' + needs: [ test-forge-unit-and-integration, test-forge-e2e, test-cast ] + steps: + - name: Notify that the workflow has failed + uses: slackapi/slack-github-action@v1.27.0 + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_SCHEDULED_TESTS_FAILURE_WEBHOOK_URL }} + with: + payload: | + { + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + } diff --git a/CHANGELOG.md b/CHANGELOG.md index 56f1be4062..8d65aae31f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### `snforge_scarb_plugin` + +#### Changed + + - `snforge_scarb_plugin` will now also emit warnings when errors occur + +## [0.34.0] - 2024-11-26 + ### Forge #### Added - `generate_random_felt()` for generating (pseudo) random felt value. - Printing information about compiling Sierra using `universal-sierra-compiler` +- Displaying backtrace when contract call fails #### Changed @@ -24,10 +33,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - You can skip `--name` flag when using `account import` - a default name will be generated. - Addresses outputted when calling `sncast account create`, `sncast deploy` and `sncast declare` are now padded to 64 characters length and prefixed with `0x0` +- Globally available configuration to store profiles to share between projects. #### Changed - Changed return type of `declare` in Cairo Deployment Scripts so it can handle already declared contracts without failing +- Allow using `show-config` command without providing rpc url ## [0.33.0] - 2024-11-04 diff --git a/Cargo.lock b/Cargo.lock index 2701a0f44f..b9fa0b1262 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,9 +121,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "ark-ec" @@ -335,9 +335,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", @@ -1320,9 +1320,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -1330,9 +1330,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -1407,7 +1407,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.13", "windows-sys 0.52.0", ] @@ -1419,9 +1419,9 @@ checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" [[package]] name = "const-hex" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" dependencies = [ "cfg-if", "cpufeatures", @@ -1593,9 +1593,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", "syn 2.0.87", @@ -1889,6 +1889,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "docs" +version = "0.34.0" +dependencies = [ + "regex", + "serde", + "serde_json", + "shell-words", + "walkdir", +] + [[package]] name = "dyn-clone" version = "1.0.17" @@ -2089,9 +2100,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -2129,7 +2140,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "forge" -version = "0.33.0" +version = "0.34.0" dependencies = [ "anyhow", "ark-ff", @@ -2157,6 +2168,7 @@ dependencies = [ "configuration", "console", "conversions", + "docs", "flatten-serde-json", "forge_runner", "fs_extra", @@ -2200,7 +2212,7 @@ dependencies = [ [[package]] name = "forge_runner" -version = "0.33.0" +version = "0.34.0" dependencies = [ "anyhow", "bimap", @@ -2216,6 +2228,7 @@ dependencies = [ "cairo-lang-sierra-to-casm", "cairo-lang-sierra-type-size", "cairo-lang-starknet", + "cairo-lang-starknet-classes", "cairo-lang-test-plugin", "cairo-lang-utils", "cairo-vm", @@ -3025,15 +3038,15 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.2.0", + "web-time", ] [[package]] @@ -3211,9 +3224,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libm" @@ -3968,9 +3981,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -4340,9 +4353,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -4407,7 +4420,7 @@ dependencies = [ "scopeguard", "smallvec", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.13", "utf8parse", "winapi", ] @@ -4609,18 +4622,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -4640,9 +4653,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -4779,6 +4792,12 @@ dependencies = [ "url", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shellexpand" version = "3.1.0" @@ -4873,7 +4892,7 @@ dependencies = [ [[package]] name = "sncast" -version = "0.33.0" +version = "0.34.0" dependencies = [ "anyhow", "async-trait", @@ -4897,6 +4916,8 @@ dependencies = [ "conversions", "ctor", "data-transformer", + "dirs", + "docs", "fs_extra", "indoc", "itertools 0.12.1", @@ -4936,7 +4957,7 @@ dependencies = [ [[package]] name = "snforge_scarb_plugin" -version = "0.33.0" +version = "0.34.0" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -5382,9 +5403,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -5479,18 +5500,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3c6efbfc763e64eb85c11c25320f0737cb7364c4b6336db90aa9ebe27a0bbd" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -5569,9 +5590,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -5806,6 +5827,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.5" @@ -5837,9 +5864,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -5995,6 +6022,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki" version = "0.22.4" diff --git a/Cargo.toml b/Cargo.toml index 9403ef2a2e..d17dd84fb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,11 @@ members = [ "crates/configuration", "crates/universal-sierra-compiler-api", "crates/snforge-scarb-plugin", + "crates/docs", ] [workspace.package] -version = "0.33.0" +version = "0.34.0" edition = "2021" repository = "https://github.com/foundry-rs/starknet-foundry" license = "MIT" @@ -52,11 +53,12 @@ cairo-lang-parser = "2.7.0" cairo-lang-macro = "0.1.0" cairo-vm = "1.0.0-rc3" cairo-annotations = "0.1.0" +dirs = "5.0.1" starknet-types-core = { version = "0.1.7", features = ["hash", "prime-bigint"] } -anyhow = "1.0.92" +anyhow = "1.0.93" assert_fs = "1.1.2" camino = { version = "1.1.9", features = ["serde1"] } -clap = { version = "4.5.20", features = ["derive"] } +clap = { version = "4.5.21", features = ["derive"] } console = "0.15.8" include_dir = "0.7.4" indoc = "2" @@ -64,15 +66,15 @@ itertools = "0.12.1" num-traits = "0.2.19" rayon = "1.10" regex = "1.11.1" -serde = { version = "1.0.214", features = ["derive"] } -serde_json = "1.0.132" +serde = { version = "1.0.215", features = ["derive"] } +serde_json = "1.0.133" starknet = { git = "https://github.com/xJonathanLEI/starknet-rs", rev = "660a732" } starknet-crypto = { git = "https://github.com/xJonathanLEI/starknet-rs", rev = "660a732" } -tempfile = "3.13.0" -thiserror = "1.0.67" -ctor = "0.2.8" -url = { "version" = "2.5.3", "features" = ["serde"] } -tokio = { version = "1.41.0", features = ["full"] } +tempfile = "3.14.0" +thiserror = "1.0.69" +ctor = "0.2.9" +url = { "version" = "2.5.4", "features" = ["serde"] } +tokio = { version = "1.41.1", features = ["full"] } tokio-util = "0.7.11" futures = "0.3.31" num-bigint = { version = "0.4.6", features = ["rand"] } @@ -82,6 +84,7 @@ project-root = "0.2.2" which = "5.0.0" conversions = { path = "./crates/conversions" } shared = { path = "./crates/shared" } +docs = { path = "./crates/docs" } test-case = "3.1.0" scarb-metadata = "1.13.0" flatten-serde-json = "0.1.0" @@ -106,10 +109,10 @@ ark-secp256k1 = "0.4.0" ark-secp256r1 = "0.4.0" openssl = { version = "0.10", features = ["vendored"] } toml_edit = "0.22.22" -axum = "0.7.7" +axum = "0.7.9" lazy_static = "1.5.0" fs2 = "0.4.3" -flate2 = "1.0.34" +flate2 = "1.0.35" k256 = { version = "0.13.4", features = ["sha256", "ecdsa", "serde"] } p256 = { version = "0.13.2", features = ["sha256", "ecdsa", "serde"] } glob = "0.3.1" @@ -119,5 +122,5 @@ fs4 = "0.7" async-trait = "0.1.83" serde_path_to_error = "0.1.16" wiremock = "0.6.0" -const-hex = "1.13.1" -indicatif = "0.17.8" +const-hex = "1.14.0" +indicatif = "0.17.9" diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cairo1_execution.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cairo1_execution.rs index 22e656fc02..4b63a11d48 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cairo1_execution.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cairo1_execution.rs @@ -1,22 +1,23 @@ +use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ + ContractClassEntryPointExecutionResult, EntryPointExecutionErrorWithLastPc, GetLastPc, + OnErrorLastPc, +}; use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; use crate::runtime_extensions::cheatable_starknet_runtime_extension::CheatableStarknetRuntimeExtension; use crate::runtime_extensions::common::get_relocated_vm_trace; -use blockifier::execution::call_info::CallInfo; -use blockifier::execution::deprecated_syscalls::hint_processor::SyscallCounter; use blockifier::execution::entry_point_execution::{ finalize_execution, initialize_execution_context, prepare_call_arguments, VmExecutionContext, }; use blockifier::{ execution::{ contract_class::{ContractClassV1, EntryPointV1}, - entry_point::{CallEntryPoint, EntryPointExecutionContext, EntryPointExecutionResult}, + entry_point::{CallEntryPoint, EntryPointExecutionContext}, errors::EntryPointExecutionError, execution_utils::Args, }, state::state_api::State, }; use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; -use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; use cairo_vm::{ hint_processor::hint_processor_definition::HintProcessor, vm::runners::cairo_runner::{CairoArg, CairoRunner, ExecutionResources}, @@ -31,7 +32,7 @@ pub fn execute_entry_point_call_cairo1( cheatnet_state: &mut CheatnetState, // Added parameter resources: &mut ExecutionResources, context: &mut EntryPointExecutionContext, -) -> EntryPointExecutionResult<(CallInfo, SyscallCounter, Option>)> { +) -> ContractClassEntryPointExecutionResult { let VmExecutionContext { mut runner, mut syscall_handler, @@ -68,7 +69,8 @@ pub fn execute_entry_point_call_cairo1( &entry_point, &args, program_extra_data_length, - )?; + ) + .on_error_get_last_pc(&mut runner)?; let vm_trace = if cheatable_runtime .extension @@ -86,6 +88,7 @@ pub fn execute_entry_point_call_cairo1( .syscall_counter .clone(); + let last_pc = runner.get_last_pc(); let call_info = finalize_execution( runner, cheatable_runtime.extended_runtime.hint_handler, @@ -94,8 +97,11 @@ pub fn execute_entry_point_call_cairo1( program_extra_data_length, )?; if call_info.execution.failed { - return Err(EntryPointExecutionError::ExecutionFailed { - error_data: call_info.execution.retdata.0, + return Err(EntryPointExecutionErrorWithLastPc { + source: EntryPointExecutionError::ExecutionFailed { + error_data: call_info.execution.retdata.0, + }, + last_pc, }); } diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/deprecated/cairo0_execution.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/deprecated/cairo0_execution.rs index 35c139c502..8a1e89cbcf 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/deprecated/cairo0_execution.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/deprecated/cairo0_execution.rs @@ -1,23 +1,21 @@ +use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ + ContractClassEntryPointExecutionResult, OnErrorLastPc, +}; use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; use crate::runtime_extensions::deprecated_cheatable_starknet_extension::runtime::{ DeprecatedExtendedRuntime, DeprecatedStarknetRuntime, }; use crate::runtime_extensions::deprecated_cheatable_starknet_extension::DeprecatedCheatableStarknetRuntimeExtension; -use blockifier::execution::call_info::CallInfo; use blockifier::execution::contract_class::ContractClassV0; use blockifier::execution::deprecated_entry_point_execution::{ finalize_execution, initialize_execution_context, prepare_call_arguments, VmExecutionContext, }; -use blockifier::execution::entry_point::{ - CallEntryPoint, EntryPointExecutionContext, EntryPointExecutionResult, -}; +use blockifier::execution::entry_point::{CallEntryPoint, EntryPointExecutionContext}; use blockifier::execution::errors::EntryPointExecutionError; use blockifier::execution::execution_utils::Args; -use blockifier::execution::syscalls::hint_processor::SyscallCounter; use blockifier::state::state_api::State; use cairo_vm::hint_processor::hint_processor_definition::HintProcessor; use cairo_vm::vm::runners::cairo_runner::{CairoArg, CairoRunner, ExecutionResources}; -use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; // blockifier/src/execution/deprecated_execution.rs:36 (execute_entry_point_call) pub fn execute_entry_point_call_cairo0( @@ -27,7 +25,7 @@ pub fn execute_entry_point_call_cairo0( cheatnet_state: &mut CheatnetState, resources: &mut ExecutionResources, context: &mut EntryPointExecutionContext, -) -> EntryPointExecutionResult<(CallInfo, SyscallCounter, Option>)> { +) -> ContractClassEntryPointExecutionResult { let VmExecutionContext { mut runner, mut syscall_handler, @@ -61,7 +59,8 @@ pub fn execute_entry_point_call_cairo0( &mut cheatable_syscall_handler, entry_point_pc, &args, - )?; + ) + .on_error_get_last_pc(&mut runner)?; let syscall_counter = cheatable_syscall_handler .extended_runtime diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs index 87f494dd6d..5aeed3dcb6 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use super::cairo1_execution::execute_entry_point_call_cairo1; use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::deprecated::cairo0_execution::execute_entry_point_call_cairo0; use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; -use crate::state::{CallTrace, CallTraceNode, CheatStatus}; +use crate::state::{CallTrace, CallTraceNode, CheatStatus, EncounteredError}; use blockifier::execution::call_info::{CallExecution, Retdata}; use blockifier::{ execution::{ @@ -17,7 +17,7 @@ use blockifier::{ }, state::state_api::State, }; -use cairo_vm::vm::runners::cairo_runner::ExecutionResources; +use cairo_vm::vm::runners::cairo_runner::{CairoRunner, ExecutionResources}; use starknet_api::{ core::ClassHash, deprecated_contract_class::EntryPointType, @@ -28,11 +28,17 @@ use std::rc::Rc; use blockifier::execution::deprecated_syscalls::hint_processor::SyscallCounter; use starknet_types_core::felt::Felt; use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; +use thiserror::Error; use conversions::FromConv; use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{AddressOrClassHash, CallResult}; use crate::runtime_extensions::common::sum_syscall_counters; use conversions::string::TryFromHexStr; +pub(crate) type ContractClassEntryPointExecutionResult = Result< + (CallInfo, SyscallCounter, Option>), + EntryPointExecutionErrorWithLastPc, +>; + // blockifier/src/execution/entry_point.rs:180 (CallEntryPoint::execute) #[allow(clippy::too_many_lines)] pub fn execute_call_entry_point( @@ -151,7 +157,15 @@ pub fn execute_call_entry_point( ); Ok(call_info) } - Err(err) => { + Err(EntryPointExecutionErrorWithLastPc { + source: err, + last_pc: pc, + }) => { + if let Some(pc) = pc { + cheatnet_state + .encountered_errors + .push(EncounteredError { pc, class_hash }); + } exit_error_call(&err, cheatnet_state, resources, entry_point); Err(err) } @@ -297,3 +311,61 @@ fn aggregate_syscall_counters(trace: &Rc>) -> SyscallCounter } result } + +#[derive(Debug, Error)] +#[error("{}", source)] +pub struct EntryPointExecutionErrorWithLastPc { + pub source: EntryPointExecutionError, + pub last_pc: Option, +} + +impl From for EntryPointExecutionErrorWithLastPc +where + T: Into, +{ + fn from(value: T) -> Self { + Self { + source: value.into(), + last_pc: None, + } + } +} + +pub(crate) trait OnErrorLastPc: Sized { + fn on_error_get_last_pc( + self, + runner: &mut CairoRunner, + ) -> Result; +} + +impl OnErrorLastPc for Result { + fn on_error_get_last_pc( + self, + runner: &mut CairoRunner, + ) -> Result { + match self { + Err(source) => { + let last_pc = runner.get_last_pc(); + + Err(EntryPointExecutionErrorWithLastPc { source, last_pc }) + } + Ok(value) => Ok(value), + } + } +} + +pub trait GetLastPc { + fn get_last_pc(&mut self) -> Option; +} + +impl GetLastPc for CairoRunner { + fn get_last_pc(&mut self) -> Option { + if self.relocated_trace.is_none() { + self.relocate(true).ok()?; + } + self.relocated_trace + .as_ref() + .and_then(|trace| trace.last()) + .map(|entry| entry.pc) + } +} diff --git a/crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/mod.rs index 0fff9cf90c..3380d88413 100644 --- a/crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/mod.rs @@ -201,7 +201,7 @@ impl<'a> DeprecatedExtensionLogic for DeprecatedCheatableStarknetRuntimeExtensio } } -impl<'a> DeprecatedCheatableStarknetRuntimeExtension<'a> { +impl DeprecatedCheatableStarknetRuntimeExtension<'_> { // crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs:233 fn execute_syscall( &mut self, diff --git a/crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/runtime.rs b/crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/runtime.rs index b5fe05a40b..d9830038df 100644 --- a/crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/runtime.rs +++ b/crates/cheatnet/src/runtime_extensions/deprecated_cheatable_starknet_extension/runtime.rs @@ -30,7 +30,7 @@ pub struct DeprecatedStarknetRuntime<'a> { pub hint_handler: DeprecatedSyscallHintProcessor<'a>, } -impl<'a> SyscallPtrAccess for DeprecatedStarknetRuntime<'a> { +impl SyscallPtrAccess for DeprecatedStarknetRuntime<'_> { fn get_mut_syscall_ptr(&mut self) -> &mut Relocatable { &mut self.hint_handler.syscall_ptr } @@ -46,7 +46,7 @@ impl<'a> SyscallPtrAccess for DeprecatedStarknetRuntime<'a> { } } -impl<'a> ResourceTracker for DeprecatedStarknetRuntime<'a> { +impl ResourceTracker for DeprecatedStarknetRuntime<'_> { fn consumed(&self) -> bool { self.hint_handler.context.vm_run_resources.consumed() } @@ -64,7 +64,7 @@ impl<'a> ResourceTracker for DeprecatedStarknetRuntime<'a> { } } -impl<'a> HintProcessorLogic for DeprecatedStarknetRuntime<'a> { +impl HintProcessorLogic for DeprecatedStarknetRuntime<'_> { fn execute_hint( &mut self, vm: &mut VirtualMachine, diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index a557ecd694..5b0fbf7189 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -320,6 +320,12 @@ pub struct TraceData { pub is_vm_trace_needed: bool, } +#[derive(Clone)] +pub struct EncounteredError { + pub pc: usize, + pub class_hash: ClassHash, +} + pub struct CheatnetState { pub cheated_execution_info_contracts: HashMap, pub global_cheated_execution_info: ExecutionInfoMock, @@ -332,6 +338,7 @@ pub struct CheatnetState { pub deploy_salt_base: u32, pub block_info: BlockInfo, pub trace_data: TraceData, + pub encountered_errors: Vec, } impl Default for CheatnetState { @@ -357,6 +364,7 @@ impl Default for CheatnetState { current_call_stack: NotEmptyCallStack::from(test_call), is_vm_trace_needed: false, }, + encountered_errors: vec![], } } } diff --git a/crates/configuration/src/lib.rs b/crates/configuration/src/lib.rs index 78ee92e1c7..3b163d3593 100644 --- a/crates/configuration/src/lib.rs +++ b/crates/configuration/src/lib.rs @@ -10,7 +10,7 @@ pub const CONFIG_FILENAME: &str = "snfoundry.toml"; /// Defined in snfoundry.toml /// Configuration not associated with any specific package -pub trait GlobalConfig { +pub trait Config { #[must_use] fn tool_name() -> &'static str; @@ -40,9 +40,9 @@ fn get_with_ownership(config: serde_json::Value, key: &str) -> Option, + profile: Option<&str>, ) -> Result { - let profile_name = profile.as_deref().unwrap_or("default"); + let profile_name = profile.unwrap_or("default"); let tool_config = get_with_ownership(raw_config, tool) .unwrap_or(serde_json::Value::Object(serde_json::Map::new())); @@ -53,9 +53,9 @@ pub fn get_profile( } } -pub fn load_global_config( - path: &Option, - profile: &Option, +pub fn load_config( + path: Option<&Utf8PathBuf>, + profile: Option<&str>, ) -> Result { let config_path = path .as_ref() @@ -146,7 +146,7 @@ pub fn search_config_upwards_relative_to(current_dir: &Utf8PathBuf) -> Result Result { search_config_upwards_relative_to(&Utf8PathBuf::try_from( - std::env::current_dir().expect("Failed to get current directory"), + env::current_dir().expect("Failed to get current directory"), )?) } @@ -243,7 +243,7 @@ mod tests { #[serde(default)] pub account: String, } - impl GlobalConfig for StubConfig { + impl Config for StubConfig { fn tool_name() -> &'static str { "stubtool" } @@ -255,9 +255,9 @@ mod tests { #[test] fn load_config_happy_case_with_profile() { let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None).unwrap(); - let config = load_global_config::( - &Some(Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), - &Some(String::from("profile1")), + let config = load_config::( + Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), + Some(&String::from("profile1")), ) .unwrap(); assert_eq!(config.account, String::from("user3")); @@ -267,9 +267,9 @@ mod tests { #[test] fn load_config_happy_case_default_profile() { let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None).unwrap(); - let config = load_global_config::( - &Some(Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), - &None, + let config = load_config::( + Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), + None, ) .unwrap(); assert_eq!(config.account, String::from("user1")); @@ -279,9 +279,9 @@ mod tests { #[test] fn load_config_not_found() { let tempdir = tempdir().expect("Failed to create a temporary directory"); - let config = load_global_config::( - &Some(Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), - &None, + let config = load_config::( + Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), + None, ) .unwrap(); @@ -310,7 +310,7 @@ mod tests { url_nested: f32, } - impl GlobalConfig for StubComplexConfig { + impl Config for StubComplexConfig { fn tool_name() -> &'static str { "stubtool" } @@ -325,9 +325,9 @@ mod tests { let temp_dir = tempdir().expect("Failed to create a temporary directory"); File::create(temp_dir.path().join(CONFIG_FILENAME)).unwrap(); - load_global_config::( - &Some(Utf8PathBuf::try_from(temp_dir.path().to_path_buf()).unwrap()), - &None, + load_config::( + Some(&Utf8PathBuf::try_from(temp_dir.path().to_path_buf()).unwrap()), + None, ) .unwrap(); } @@ -344,9 +344,9 @@ mod tests { ) .expect("Failed to copy config file to temp dir"); // missing env variables - if load_global_config::( - &Some(Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), - &Some(String::from("with-envs")), + if load_config::( + Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), + Some(&String::from("with-envs")), ) .is_ok() { @@ -359,9 +359,9 @@ mod tests { env::set_var("VALUE_FLOAT123132", "321.312"); env::set_var("VALUE_BOOL1231321", "true"); env::set_var("VALUE_BOOL1231322", "false"); - let config = load_global_config::( - &Some(Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), - &Some(String::from("with-envs")), + let config = load_config::( + Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), + Some(&String::from("with-envs")), ) .unwrap(); assert_eq!(config.url, String::from("nfsaufbnsailfbsbksdabfnkl")); diff --git a/crates/conversions/cairo-serde-macros/Cargo.toml b/crates/conversions/cairo-serde-macros/Cargo.toml index 4da7e3d161..8fdba8a168 100644 --- a/crates/conversions/cairo-serde-macros/Cargo.toml +++ b/crates/conversions/cairo-serde-macros/Cargo.toml @@ -9,4 +9,4 @@ proc-macro = true [dependencies] syn = "2.0.79" quote = "1.0.37" -proc-macro2 = "1.0.89" +proc-macro2 = "1.0.92" diff --git a/crates/conversions/src/serde/deserialize.rs b/crates/conversions/src/serde/deserialize.rs index 108a1dbb48..8576efcf0b 100644 --- a/crates/conversions/src/serde/deserialize.rs +++ b/crates/conversions/src/serde/deserialize.rs @@ -24,7 +24,7 @@ pub trait CairoDeserialize: Sized { fn deserialize(reader: &mut BufferReader<'_>) -> BufferReadResult; } -impl<'b> BufferReader<'b> { +impl BufferReader<'_> { #[must_use] pub fn new<'a>(buffer: &'a [Felt]) -> BufferReader<'a> { BufferReader::<'a> { buffer, idx: 0 } diff --git a/crates/docs/Cargo.toml b/crates/docs/Cargo.toml new file mode 100644 index 0000000000..41e8b672b1 --- /dev/null +++ b/crates/docs/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "docs" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +regex = "1.11.1" +shell-words = "1.1.0" +walkdir.workspace = true +serde.workspace = true +serde_json.workspace = true + +[features] +testing = [] diff --git a/crates/docs/src/lib.rs b/crates/docs/src/lib.rs new file mode 100644 index 0000000000..684d3b527e --- /dev/null +++ b/crates/docs/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(feature = "testing")] +pub mod snippet; +#[cfg(feature = "testing")] +pub mod validation; + +#[cfg(feature = "testing")] +pub mod utils; diff --git a/crates/docs/src/snippet.rs b/crates/docs/src/snippet.rs new file mode 100644 index 0000000000..2b03f74534 --- /dev/null +++ b/crates/docs/src/snippet.rs @@ -0,0 +1,89 @@ +use regex::Regex; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug)] +pub struct SnippetType(String); + +impl SnippetType { + #[must_use] + pub fn forge() -> Self { + SnippetType("snforge".to_string()) + } + + #[must_use] + pub fn sncast() -> Self { + SnippetType("sncast".to_string()) + } + + #[must_use] + pub fn as_str(&self) -> &str { + &self.0 + } + + #[must_use] + pub fn get_re(&self) -> Regex { + // The regex pattern is used to match the snippet, its config and the output. Example: + // + // ```shell + // $ snforge or sncast command with args... + // ``` + //
+ // Output: + // ```shell + // Output of the command... + // ``` + //
+ + let escaped_command = regex::escape(self.as_str()); + let pattern = format!( + r"(?ms)^(?:\n)?```shell\n\$ (?P{escaped_command} .+?)\n```(?:\s*
\nOutput:<\/summary>\n\n```shell\n(?P[\s\S]+?)\n```[\s]*<\/details>)?" + ); + + Regex::new(&pattern).unwrap() + } +} + +#[derive(Debug, Deserialize, Serialize, Default)] +pub struct SnippetConfig { + pub ignored: Option, + pub package_name: Option, +} + +#[derive(Debug)] +pub struct Snippet { + pub command: String, + pub output: Option, + pub file_path: String, + pub line_start: usize, + pub snippet_type: SnippetType, + pub config: SnippetConfig, +} + +impl Snippet { + #[must_use] + pub fn to_command_args(&self) -> Vec { + let cleaned_command = self + .command + .lines() + .collect::>() + .join(" ") + .replace(" \\", ""); + + shell_words::split(&cleaned_command) + .expect("Failed to parse snippet string") + .into_iter() + .map(|arg| arg.trim().to_string()) + .collect() + } + + #[must_use] + pub fn capture_package_from_output(&self) -> Option { + let re = + Regex::new(r"Collected \d+ test\(s\) from ([a-zA-Z_][a-zA-Z0-9_]*) package").unwrap(); + + re.captures_iter(self.output.as_ref()?) + .filter_map(|caps| caps.get(1)) + .last() + .map(|m| m.as_str().to_string()) + } +} diff --git a/crates/docs/src/utils.rs b/crates/docs/src/utils.rs new file mode 100644 index 0000000000..9b5b3bcfaa --- /dev/null +++ b/crates/docs/src/utils.rs @@ -0,0 +1,41 @@ +use std::{env, path::PathBuf}; + +use crate::snippet::Snippet; + +#[must_use] +pub fn get_nth_ancestor(levels_up: usize) -> PathBuf { + let mut dir = env::current_dir().expect("Failed to get the current directory"); + + for _ in 0..levels_up { + dir = dir + .parent() + .expect("Failed to navigate to parent directory") + .to_owned(); + } + + dir +} + +pub fn assert_valid_snippet(condition: bool, snippet: &Snippet, err_message: &str) { + assert!( + condition, + "Found invalid {} snippet in the docs at {}:{}:1\n{}", + snippet.snippet_type.as_str(), + snippet.file_path, + snippet.line_start, + err_message + ); +} + +pub fn print_success_message(snippets_len: usize, tool_name: &str) { + println!("Successfully validated {snippets_len} {tool_name} docs snippets"); +} + +pub fn print_skipped_snippet_message(snippet: &Snippet) { + println!( + "Skipped validation of {} snippet in the docs in file: {} at line {}", + snippet.snippet_type.as_str(), + snippet.file_path, + snippet.line_start, + ); +} diff --git a/crates/docs/src/validation.rs b/crates/docs/src/validation.rs new file mode 100644 index 0000000000..bf141b0e41 --- /dev/null +++ b/crates/docs/src/validation.rs @@ -0,0 +1,83 @@ +use crate::snippet::{Snippet, SnippetConfig, SnippetType}; +use regex::Regex; +use std::sync::LazyLock; +use std::{fs, io, path::Path}; + +const EXTENSION: Option<&str> = Some("md"); + +pub fn extract_snippets_from_file( + file_path: &Path, + snippet_type: &SnippetType, +) -> io::Result> { + let content = fs::read_to_string(file_path)?; + let file_path_str = file_path + .to_str() + .expect("Failed to get file path") + .to_string(); + + let snippets = snippet_type + .get_re() + .captures_iter(&content) + .filter_map(|caps| { + let match_start = caps.get(0)?.start(); + let config_str = caps + .name("config") + .map_or_else(String::new, |m| m.as_str().to_string()); + let command_match = caps.name("command")?; + let output = caps.name("output").map(|m| { + static GAS_RE: LazyLock = + LazyLock::new(|| Regex::new(r"gas: ~\d+").unwrap()); + static EXECUTION_RESOURCES_RE: LazyLock = LazyLock::new(|| { + Regex::new(r"(steps|memory holes|builtins|syscalls): (\d+|\(.+\))").unwrap() + }); + + let output = GAS_RE.replace_all(m.as_str(), "gas: ~[..]").to_string(); + EXECUTION_RESOURCES_RE + .replace_all(output.as_str(), "${1}: [..]") + .to_string() + }); + + let config = if config_str.is_empty() { + SnippetConfig::default() + } else { + serde_json::from_str(&config_str).expect("Failed to parse snippet config") + }; + + Some(Snippet { + command: command_match.as_str().to_string(), + output, + file_path: file_path_str.clone(), + line_start: content[..match_start].lines().count() + 1, + snippet_type: snippet_type.clone(), + config, + }) + }) + .collect(); + + Ok(snippets) +} + +pub fn extract_snippets_from_directory( + dir_path: &Path, + snippet_type: &SnippetType, +) -> io::Result> { + let mut all_snippets = Vec::new(); + + let files = walkdir::WalkDir::new(dir_path) + .into_iter() + .map(|entry| entry.expect("Failed to read directory")) + .filter(|entry| entry.path().is_file()); + + for file in files { + let path = file.path(); + + if EXTENSION.map_or(true, |ext| { + path.extension().and_then(|path_ext| path_ext.to_str()) == Some(ext) + }) { + let snippets = extract_snippets_from_file(path, snippet_type)?; + all_snippets.extend(snippets); + } + } + + Ok(all_snippets) +} diff --git a/crates/forge-runner/Cargo.toml b/crates/forge-runner/Cargo.toml index 929c0f720f..a9e35b7012 100644 --- a/crates/forge-runner/Cargo.toml +++ b/crates/forge-runner/Cargo.toml @@ -19,6 +19,7 @@ cairo-lang-sierra-type-size.workspace = true cairo-lang-sierra-gas.workspace = true cairo-lang-sierra-ap-change.workspace = true cairo-lang-test-plugin.workspace = true +cairo-lang-starknet-classes.workspace = true cairo-annotations.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true diff --git a/crates/forge-runner/src/backtrace.rs b/crates/forge-runner/src/backtrace.rs new file mode 100644 index 0000000000..7afad162bb --- /dev/null +++ b/crates/forge-runner/src/backtrace.rs @@ -0,0 +1,226 @@ +use anyhow::{Context, Result}; +use cairo_annotations::annotations::coverage::{ + CodeLocation, ColumnNumber, CoverageAnnotationsV1, LineNumber, VersionedCoverageAnnotations, +}; +use cairo_annotations::annotations::profiler::{ + FunctionName, ProfilerAnnotationsV1, VersionedProfilerAnnotations, +}; +use cairo_annotations::annotations::TryFromDebugInfo; +use cairo_lang_sierra::program::StatementIdx; +use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; +use cairo_lang_starknet_classes::contract_class::ContractClass; +use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; +use cheatnet::state::EncounteredError; +use indoc::indoc; +use itertools::Itertools; +use rayon::prelude::*; +use starknet_api::core::ClassHash; +use std::collections::HashMap; +use std::fmt::Display; +use std::{env, fmt}; + +const BACKTRACE_ENV: &str = "SNFORGE_BACKTRACE"; + +pub fn add_backtrace_footer( + message: String, + contracts_data: &ContractsData, + encountered_errors: &[EncounteredError], +) -> String { + if encountered_errors.is_empty() { + return message; + } + + let is_backtrace_enabled = env::var(BACKTRACE_ENV).is_ok_and(|value| value == "1"); + if !is_backtrace_enabled { + return format!( + "{message}\nnote: run with `{BACKTRACE_ENV}=1` environment variable to display a backtrace", + ); + } + + BacktraceContractRepository::new(contracts_data, encountered_errors) + .map(|repository| { + encountered_errors + .iter() + .filter_map(|error| repository.get_backtrace(error.pc, error.class_hash)) + .map(|backtrace| backtrace.to_string()) + .collect::>() + .join("\n") + }) + .map_or_else( + |err| format!("{message}\nfailed to create backtrace: {err}"), + |backtraces| format!("{message}\n{backtraces}"), + ) +} + +struct ContractBacktraceData { + contract_name: String, + casm_debug_info_start_offsets: Vec, + coverage_annotations: CoverageAnnotationsV1, + profiler_annotations: ProfilerAnnotationsV1, +} + +impl ContractBacktraceData { + fn new(class_hash: &ClassHash, contracts_data: &ContractsData) -> Result { + let contract_name = contracts_data + .get_contract_name(class_hash) + .context(format!( + "failed to get contract name for class hash: {class_hash}" + ))? + .clone(); + + let contract_artifacts = contracts_data + .get_artifacts(&contract_name) + .context(format!( + "failed to get artifacts for contract name: {contract_name}" + ))?; + + let contract_class = serde_json::from_str::(&contract_artifacts.sierra)?; + + let sierra_debug_info = contract_class + .sierra_program_debug_info + .as_ref() + .context("debug info not found")?; + + let VersionedCoverageAnnotations::V1(coverage_annotations) = + VersionedCoverageAnnotations::try_from_debug_info(sierra_debug_info).context(indoc! { + "perhaps the contract was compiled without the following entry in Scarb.toml under [profile.dev.cairo]: + unstable-add-statements-code-locations-debug-info = true + + or scarb version is less than 2.8.0 + " + })?; + + let VersionedProfilerAnnotations::V1(profiler_annotations) = + VersionedProfilerAnnotations::try_from_debug_info(sierra_debug_info).context(indoc! { + "perhaps the contract was compiled without the following entry in Scarb.toml under [profile.dev.cairo]: + unstable-add-statements-functions-debug-info = true + + or scarb version is less than 2.8.0 + " + })?; + + // Not optimal, but USC doesn't produce debug info for the contract class + let (_, debug_info) = CasmContractClass::from_contract_class_with_debug_info( + contract_class, + true, + usize::MAX, + )?; + + let casm_debug_info_start_offsets = debug_info + .sierra_statement_info + .iter() + .map(|statement_debug_info| statement_debug_info.start_offset) + .collect(); + + Ok(Self { + contract_name, + casm_debug_info_start_offsets, + coverage_annotations, + profiler_annotations, + }) + } + + fn backtrace_from(&self, pc: usize) -> Option { + let sierra_statement_idx = StatementIdx( + self.casm_debug_info_start_offsets + .partition_point(|start_offset| *start_offset < pc - 1) + .saturating_sub(1), + ); + + let code_locations = self + .coverage_annotations + .statements_code_locations + .get(&sierra_statement_idx)?; + + let function_names = self + .profiler_annotations + .statements_functions + .get(&sierra_statement_idx)?; + + let stack = code_locations + .iter() + .zip(function_names) + .map(|(code_location, function_name)| Backtrace { + code_location, + function_name, + }) + .collect(); + + Some(BacktraceStack { + pc, + contract_name: &self.contract_name, + stack, + }) + } +} + +struct BacktraceContractRepository(HashMap); + +impl BacktraceContractRepository { + fn new( + contracts_data: &ContractsData, + encountered_errors: &[EncounteredError], + ) -> Result { + Ok(Self( + encountered_errors + .iter() + .map(|error| error.class_hash) + .unique() + .collect::>() + .par_iter() + .map(|class_hash| { + ContractBacktraceData::new(class_hash, contracts_data) + .map(|contract_data| (*class_hash, contract_data)) + }) + .collect::>()?, + )) + } + + fn get_backtrace(&self, pc: usize, class_hash: ClassHash) -> Option { + self.0.get(&class_hash)?.backtrace_from(pc) + } +} + +struct Backtrace<'a> { + code_location: &'a CodeLocation, + function_name: &'a FunctionName, +} + +struct BacktraceStack<'a> { + pc: usize, + contract_name: &'a str, + stack: Vec>, +} + +impl Display for Backtrace<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let function_name = &self.function_name.0; + let path = truncate_at_char(&self.code_location.0 .0, '['); + let line = self.code_location.1.start.line + LineNumber(1); // most editors start line numbers from 1 + let col = self.code_location.1.start.col + ColumnNumber(1); // most editors start column numbers from 1 + + write!(f, "{function_name}\n at {path}:{line}:{col}",) + } +} + +impl Display for BacktraceStack<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "error occurred in contract '{}' at pc: '{}'", + self.contract_name, self.pc + )?; + writeln!(f, "stack backtrace:")?; + for (i, backtrace) in self.stack.iter().enumerate() { + writeln!(f, " {i}: {backtrace}")?; + } + Ok(()) + } +} + +fn truncate_at_char(input: &str, delimiter: char) -> &str { + match input.find(delimiter) { + Some(index) => &input[..index], + None => input, + } +} diff --git a/crates/forge-runner/src/build_trace_data.rs b/crates/forge-runner/src/build_trace_data.rs index bb4541795a..845775c234 100644 --- a/crates/forge-runner/src/build_trace_data.rs +++ b/crates/forge-runner/src/build_trace_data.rs @@ -40,7 +40,7 @@ pub const TEST_CODE_FUNCTION_NAME: &str = "SNFORGE_TEST_CODE_FUNCTION"; pub fn build_profiler_call_trace( value: &Rc>, contracts_data: &ContractsData, - maybe_versioned_program_path: &Option, + maybe_versioned_program_path: Option<&VersionedProgramPath>, ) -> ProfilerCallTrace { let value = value.borrow(); @@ -79,7 +79,7 @@ fn build_cairo_execution_info( entry_point: &CallEntryPoint, vm_trace: Option>, contracts_data: &ContractsData, - maybe_test_sierra_program_path: &Option, + maybe_test_sierra_program_path: Option<&VersionedProgramPath>, run_with_call_header: bool, ) -> Option { let contract_name = get_contract_name(entry_point.class_hash, contracts_data); @@ -104,13 +104,12 @@ fn build_cairo_execution_info( fn get_source_sierra_path( contract_name: &str, contracts_data: &ContractsData, - maybe_versioned_program_path: &Option, + maybe_versioned_program_path: Option<&VersionedProgramPath>, ) -> Option { if contract_name == TEST_CODE_CONTRACT_NAME { Some( maybe_versioned_program_path - .clone() - .map_or_else(Utf8PathBuf::new, Into::into), + .map_or_else(Utf8PathBuf::new, |v| Utf8PathBuf::from(v.clone())), ) } else { contracts_data @@ -122,7 +121,7 @@ fn get_source_sierra_path( fn build_profiler_call_trace_node( value: &CallTraceNode, contracts_data: &ContractsData, - maybe_versioned_program_path: &Option, + maybe_versioned_program_path: Option<&VersionedProgramPath>, ) -> ProfilerCallTraceNode { match value { CallTraceNode::EntryPointCall(trace) => ProfilerCallTraceNode::EntryPointCall( diff --git a/crates/forge-runner/src/gas.rs b/crates/forge-runner/src/gas.rs index 46d58383ee..99997d9c78 100644 --- a/crates/forge-runner/src/gas.rs +++ b/crates/forge-runner/src/gas.rs @@ -146,7 +146,7 @@ fn get_l1_data_cost( } pub fn check_available_gas( - available_gas: &Option, + available_gas: Option, summary: TestCaseSummary, ) -> TestCaseSummary { match summary { diff --git a/crates/forge-runner/src/lib.rs b/crates/forge-runner/src/lib.rs index 1c231eea19..8608f685e6 100644 --- a/crates/forge-runner/src/lib.rs +++ b/crates/forge-runner/src/lib.rs @@ -33,6 +33,7 @@ pub mod profiler_api; pub mod test_case_summary; pub mod test_target_summary; +mod backtrace; mod fuzzer; mod gas; pub mod printing; diff --git a/crates/forge-runner/src/package_tests/raw.rs b/crates/forge-runner/src/package_tests/raw.rs index d2854465f9..b669d1eec8 100644 --- a/crates/forge-runner/src/package_tests/raw.rs +++ b/crates/forge-runner/src/package_tests/raw.rs @@ -3,7 +3,6 @@ use cairo_lang_sierra::program::ProgramArtifact; use camino::Utf8PathBuf; /// these structs are representation of scarb output for `scarb build --test` - /// produced by scarb pub struct TestTargetRaw { pub sierra_file_path: Utf8PathBuf, diff --git a/crates/forge-runner/src/running.rs b/crates/forge-runner/src/running.rs index d286a70bf3..8899e3b722 100644 --- a/crates/forge-runner/src/running.rs +++ b/crates/forge-runner/src/running.rs @@ -1,3 +1,4 @@ +use crate::backtrace::add_backtrace_footer; use crate::build_trace_data::test_sierra_program_path::VersionedProgramPath; use crate::forge_config::{RuntimeConfig, TestRunnerConfig}; use crate::gas::calculate_used_gas; @@ -21,7 +22,9 @@ use cheatnet::runtime_extensions::forge_runtime_extension::{ get_all_used_resources, update_top_call_execution_resources, update_top_call_l1_resources, update_top_call_vm_trace, ForgeExtension, ForgeRuntime, }; -use cheatnet::state::{BlockInfoReader, CallTrace, CheatnetState, ExtendedStateReader}; +use cheatnet::state::{ + BlockInfoReader, CallTrace, CheatnetState, EncounteredError, ExtendedStateReader, +}; use entry_code::create_entry_code; use hints::{hints_by_representation, hints_to_params}; use runtime::starknet::context::{build_context, set_max_steps}; @@ -78,7 +81,7 @@ pub fn run_test( &case, vec![], &test_runner_config.contracts_data, - &maybe_versioned_program_path, + maybe_versioned_program_path.as_ref().as_ref(), ) }) } @@ -119,7 +122,7 @@ pub(crate) fn run_fuzz_test( &case, args, &test_runner_config.contracts_data, - &maybe_versioned_program_path, + maybe_versioned_program_path.as_ref().as_ref(), ) }) } @@ -129,6 +132,7 @@ pub struct RunResultWithInfo { pub(crate) call_trace: Rc>, pub(crate) gas_used: u128, pub(crate) used_resources: UsedResources, + pub(crate) encountered_errors: Vec, } #[allow(clippy::too_many_lines)] @@ -154,7 +158,7 @@ pub fn run_test_case( dict_state_reader: cheatnet_constants::build_testing_state(), fork_state_reader: get_fork_state_reader( runtime_config.cache_dir, - &case.config.fork_config, + case.config.fork_config.as_ref(), )?, }; let block_info = state_reader.get_block_info()?; @@ -246,6 +250,14 @@ pub fn run_test_case( Err(err) => Err(err), }; + let encountered_errors = forge_runtime + .extended_runtime + .extended_runtime + .extension + .cheatnet_state + .encountered_errors + .clone(); + let call_trace_ref = get_call_trace_ref(&mut forge_runtime); update_top_call_execution_resources(&mut forge_runtime); @@ -269,6 +281,7 @@ pub fn run_test_case( gas_used: gas, used_resources, call_trace: call_trace_ref, + encountered_errors, }) } @@ -277,7 +290,7 @@ fn extract_test_case_summary( case: &TestCaseWithResolvedConfig, args: Vec, contracts_data: &ContractsData, - maybe_versioned_program_path: &Option, + maybe_versioned_program_path: Option<&VersionedProgramPath>, ) -> TestCaseSummary { match run_result { Ok(result_with_info) => { @@ -289,6 +302,7 @@ fn extract_test_case_summary( result_with_info.gas_used, result_with_info.used_resources, &result_with_info.call_trace, + &result_with_info.encountered_errors, contracts_data, maybe_versioned_program_path, ), @@ -298,7 +312,14 @@ fn extract_test_case_summary( msg: Some(format!( "\n {}\n", error.to_string().replace(" Custom Hint Error: ", "\n ") - )), + )) + .map(|msg| { + add_backtrace_footer( + msg, + contracts_data, + &result_with_info.encountered_errors, + ) + }), arguments: args, test_statistics: (), }, @@ -317,7 +338,7 @@ fn extract_test_case_summary( fn get_fork_state_reader( cache_dir: &Utf8Path, - fork_config: &Option, + fork_config: Option<&ResolvedForkConfig>, ) -> Result> { fork_config .as_ref() diff --git a/crates/forge-runner/src/test_case_summary.rs b/crates/forge-runner/src/test_case_summary.rs index 78340772c9..5d37a7b140 100644 --- a/crates/forge-runner/src/test_case_summary.rs +++ b/crates/forge-runner/src/test_case_summary.rs @@ -1,3 +1,4 @@ +use crate::backtrace::add_backtrace_footer; use crate::build_trace_data::build_profiler_call_trace; use crate::build_trace_data::test_sierra_program_path::VersionedProgramPath; use crate::expected_result::{ExpectedPanicValue, ExpectedTestResult}; @@ -8,7 +9,7 @@ use cairo_lang_runner::short_string::as_cairo_short_string; use cairo_lang_runner::{RunResult, RunResultValue}; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; -use cheatnet::state::CallTrace as InternalCallTrace; +use cheatnet::state::{CallTrace as InternalCallTrace, EncounteredError}; use conversions::byte_array::ByteArray; use num_traits::Pow; use shared::utils::build_readable_text; @@ -216,11 +217,13 @@ impl TestCaseSummary { gas: u128, used_resources: UsedResources, call_trace: &Rc>, + encountered_errors: &[EncounteredError], contracts_data: &ContractsData, - maybe_versioned_program_path: &Option, + maybe_versioned_program_path: Option<&VersionedProgramPath>, ) -> Self { let name = test_case.name.clone(); - let msg = extract_result_data(&run_result, &test_case.config.expected_result); + let msg = extract_result_data(&run_result, &test_case.config.expected_result) + .map(|msg| add_backtrace_footer(msg, contracts_data, encountered_errors)); match run_result.value { RunResultValue::Success(_) => match &test_case.config.expected_result { ExpectedTestResult::Success => { @@ -237,7 +240,7 @@ impl TestCaseSummary { maybe_versioned_program_path, )), }; - check_available_gas(&test_case.config.available_gas, summary) + check_available_gas(test_case.config.available_gas, summary) } ExpectedTestResult::Panics(_) => TestCaseSummary::Failed { name, diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index f33a2dc258..3c8d119141 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -83,3 +83,4 @@ tempfile.workspace = true cairo-lang-starknet-classes.workspace = true walkdir.workspace = true test-case.workspace = true +docs = { workspace = true, features = ["testing"] } diff --git a/crates/forge/src/init.rs b/crates/forge/src/init.rs index c427d8c29e..b36154c726 100644 --- a/crates/forge/src/init.rs +++ b/crates/forge/src/init.rs @@ -5,6 +5,7 @@ use include_dir::{include_dir, Dir}; use indoc::formatdoc; use scarb_api::ScarbCommand; use semver::Version; +use shared::consts::FREE_RPC_PROVIDER_URL; use std::env; use std::fs::{self, OpenOptions}; use std::io::Write; @@ -20,16 +21,19 @@ fn create_snfoundry_manifest(path: &PathBuf) -> Result<()> { fs::write( path, formatdoc! {r#" - # Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html for more information - - # [sncast.myprofile1] # Define a profile name - # url = "http://127.0.0.1:5050/" # Url of the RPC provider - # accounts_file = "../account-file" # Path to the file with the account data - # account = "mainuser" # Account from `accounts_file` or default account file that will be used for the transactions - # keystore = "~/keystore" # Path to the keystore file - # wait_params = {{ timeout = 500, retry_interval = 10 }} # Wait for submitted transaction parameters - # block_explorer = "StarkScan" # Block explorer service used to display links to transaction details - "# + # Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html + # and https://foundry-rs.github.io/starknet-foundry/projects/configuration.html for more information + + # [sncast.default] # Define a profile name + # url = "{default_rpc_url}" # Url of the RPC provider + # accounts-file = "../account-file" # Path to the file with the account data + # account = "mainuser" # Account from `accounts_file` or default account file that will be used for the transactions + # keystore = "~/keystore" # Path to the keystore file + # wait-params = {{ timeout = 300, retry-interval = 10 }} # Wait for submitted transaction parameters + # block-explorer = "StarkScan" # Block explorer service used to display links to transaction details + # show-explorer-links = true # Print links pointing to pages with transaction details in the chosen block explorer + "#, + default_rpc_url = FREE_RPC_PROVIDER_URL, }, )?; diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 6753def0f8..13eb286806 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -53,7 +53,7 @@ Report bugs: https://github.com/foundry-rs/starknet-foundry/issues/new/choose\ )] #[command(about = "snforge - a testing tool for Starknet contracts", long_about = None)] #[clap(name = "snforge")] -struct Cli { +pub struct Cli { #[command(subcommand)] subcommand: ForgeSubcommand, } diff --git a/crates/forge/tests/data/backtrace_panic/Scarb.toml b/crates/forge/tests/data/backtrace_panic/Scarb.toml new file mode 100644 index 0000000000..db687cf174 --- /dev/null +++ b/crates/forge/tests/data/backtrace_panic/Scarb.toml @@ -0,0 +1,22 @@ +[package] +name = "backtrace_panic" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" + +[profile.dev.cairo] +unstable-add-statements-functions-debug-info = true +unstable-add-statements-code-locations-debug-info = true diff --git a/crates/forge/tests/data/backtrace_panic/src/lib.cairo b/crates/forge/tests/data/backtrace_panic/src/lib.cairo new file mode 100644 index 0000000000..5517a8fd35 --- /dev/null +++ b/crates/forge/tests/data/backtrace_panic/src/lib.cairo @@ -0,0 +1,61 @@ +#[starknet::interface] +pub trait IOuterContract { + fn outer(self: @TState, contract_address: starknet::ContractAddress); +} + +#[starknet::contract] +pub mod OuterContract { + use super::{IInnerContractDispatcher, IInnerContractDispatcherTrait}; + + #[storage] + pub struct Storage {} + + #[abi(embed_v0)] + impl OuterContract of super::IOuterContract { + fn outer(self: @ContractState, contract_address: starknet::ContractAddress) { + let dispatcher = IInnerContractDispatcher { contract_address }; + dispatcher.inner(); + } + } +} + +#[starknet::interface] +pub trait IInnerContract { + fn inner(self: @TState); +} + +#[starknet::contract] +pub mod InnerContract { + #[storage] + pub struct Storage {} + + #[abi(embed_v0)] + impl InnerContract of super::IInnerContract { + fn inner(self: @ContractState) { + inner_call() + } + } + + fn inner_call() { + assert(1 != 1, 'aaaa'); + } +} + +#[cfg(test)] +mod Test { + use snforge_std::cheatcodes::contract_class::DeclareResultTrait; + use snforge_std::{ContractClassTrait, declare}; + use super::{IOuterContractDispatcher, IOuterContractDispatcherTrait}; + + #[test] + fn test_unwrapped_call_contract_syscall() { + let contract_inner = declare("InnerContract").unwrap().contract_class(); + let (contract_address_inner, _) = contract_inner.deploy(@array![]).unwrap(); + + let contract_outer = declare("OuterContract").unwrap().contract_class(); + let (contract_address_outer, _) = contract_outer.deploy(@array![]).unwrap(); + + let dispatcher = IOuterContractDispatcher { contract_address: contract_address_outer }; + dispatcher.outer(contract_address_inner); + } +} diff --git a/crates/forge/tests/data/backtrace_vm_error/Scarb.toml b/crates/forge/tests/data/backtrace_vm_error/Scarb.toml new file mode 100644 index 0000000000..942939e25e --- /dev/null +++ b/crates/forge/tests/data/backtrace_vm_error/Scarb.toml @@ -0,0 +1,22 @@ +[package] +name = "backtrace_vm_error" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" + +[profile.dev.cairo] +unstable-add-statements-functions-debug-info = true +unstable-add-statements-code-locations-debug-info = true diff --git a/crates/forge/tests/data/backtrace_vm_error/src/lib.cairo b/crates/forge/tests/data/backtrace_vm_error/src/lib.cairo new file mode 100644 index 0000000000..ec8559e832 --- /dev/null +++ b/crates/forge/tests/data/backtrace_vm_error/src/lib.cairo @@ -0,0 +1,68 @@ +#[starknet::interface] +pub trait IOuterContract { + fn outer(self: @TState, contract_address: starknet::ContractAddress); +} + +#[starknet::contract] +pub mod OuterContract { + use super::{IInnerContractDispatcher, IInnerContractDispatcherTrait}; + + #[storage] + pub struct Storage {} + + #[abi(embed_v0)] + impl OuterContract of super::IOuterContract { + fn outer(self: @ContractState, contract_address: starknet::ContractAddress) { + let dispatcher = IInnerContractDispatcher { contract_address }; + dispatcher.inner(); + } + } +} + +#[starknet::interface] +pub trait IInnerContract { + fn inner(self: @TState); +} + +#[starknet::contract] +pub mod InnerContract { + use starknet::SyscallResultTrait; + use starknet::syscalls::call_contract_syscall; + + #[storage] + pub struct Storage {} + + #[abi(embed_v0)] + impl InnerContract of super::IInnerContract { + fn inner(self: @ContractState) { + inner_call() + } + } + + fn inner_call() { + let this = starknet::get_contract_address(); + let selector = selector!("nonexistent"); + let calldata = array![].span(); + + call_contract_syscall(this, selector, calldata).unwrap_syscall(); + } +} + +#[cfg(test)] +mod Test { + use snforge_std::cheatcodes::contract_class::DeclareResultTrait; + use snforge_std::{ContractClassTrait, declare}; + use super::{IOuterContractDispatcher, IOuterContractDispatcherTrait}; + + #[test] + fn test_unwrapped_call_contract_syscall() { + let contract_inner = declare("InnerContract").unwrap().contract_class(); + let (contract_address_inner, _) = contract_inner.deploy(@array![]).unwrap(); + + let contract_outer = declare("OuterContract").unwrap().contract_class(); + let (contract_address_outer, _) = contract_outer.deploy(@array![]).unwrap(); + + let dispatcher = IOuterContractDispatcher { contract_address: contract_address_outer }; + dispatcher.outer(contract_address_inner); + } +} diff --git a/crates/forge/tests/e2e/backtrace.rs b/crates/forge/tests/e2e/backtrace.rs new file mode 100644 index 0000000000..f591ea383d --- /dev/null +++ b/crates/forge/tests/e2e/backtrace.rs @@ -0,0 +1,126 @@ +use super::common::runner::{setup_package, test_runner}; +use assert_fs::fixture::{FileWriteStr, PathChild}; +use indoc::indoc; +use shared::test_utils::output_assert::assert_stdout_contains; +use std::fs; +use toml_edit::{value, DocumentMut}; + +#[test] +#[cfg_attr(not(feature = "scarb_2_8_3"), ignore)] +fn test_backtrace_missing_env() { + let temp = setup_package("backtrace_vm_error"); + + let output = test_runner(&temp).assert().failure(); + + assert_stdout_contains( + output, + indoc! { + "Failure data: + (0x454e545259504f494e545f4e4f545f464f554e44 ('ENTRYPOINT_NOT_FOUND'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) + note: run with `SNFORGE_BACKTRACE=1` environment variable to display a backtrace" + }, + ); +} + +#[test] +#[cfg_attr(not(feature = "scarb_2_8_3"), ignore)] +fn test_backtrace() { + let temp = setup_package("backtrace_vm_error"); + + let output = test_runner(&temp) + .env("SNFORGE_BACKTRACE", "1") + .assert() + .failure(); + + assert_stdout_contains( + output, + indoc! { + "Failure data: + (0x454e545259504f494e545f4e4f545f464f554e44 ('ENTRYPOINT_NOT_FOUND'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) + error occurred in contract 'InnerContract' at pc: '72' + stack backtrace: + 0: backtrace_vm_error::InnerContract::inner_call + at [..]/src/lib.cairo:47:9 + 1: backtrace_vm_error::InnerContract::InnerContract::inner + at [..]/src/lib.cairo:38:13 + 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner + at [..]/src/lib.cairo:37:9 + + error occurred in contract 'OuterContract' at pc: '107' + stack backtrace: + 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner + at [..]/src/lib.cairo:22:1 + 1: backtrace_vm_error::OuterContract::OuterContract::outer + at [..]/src/lib.cairo:17:13 + 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer + at [..]/src/lib.cairo:15:9" + }, + ); +} + +#[test] +#[cfg_attr(not(feature = "scarb_2_8_3"), ignore)] +fn test_wrong_scarb_toml_configuration() { + let temp = setup_package("backtrace_vm_error"); + + let manifest_path = temp.child("Scarb.toml"); + + let mut scarb_toml = fs::read_to_string(&manifest_path) + .unwrap() + .parse::() + .unwrap(); + + scarb_toml["profile"]["dev"]["cairo"]["unstable-add-statements-code-locations-debug-info"] = + value(false); + + manifest_path.write_str(&scarb_toml.to_string()).unwrap(); + + let output = test_runner(&temp) + .env("SNFORGE_BACKTRACE", "1") + .assert() + .failure(); + + assert_stdout_contains( + output, + indoc! { + "Failure data: + (0x454e545259504f494e545f4e4f545f464f554e44 ('ENTRYPOINT_NOT_FOUND'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) + failed to create backtrace: perhaps the contract was compiled without the following entry in Scarb.toml under [profile.dev.cairo]: + unstable-add-statements-code-locations-debug-info = true + + or scarb version is less than 2.8.0" + }, + ); +} + +#[test] +#[cfg_attr(not(feature = "scarb_2_8_3"), ignore)] +fn test_backtrace_panic() { + let temp = setup_package("backtrace_panic"); + + let output = test_runner(&temp) + .env("SNFORGE_BACKTRACE", "1") + .assert() + .failure(); + + assert_stdout_contains( + output, + indoc! { + "Failure data: + 0x61616161 ('aaaa') + error occurred in contract 'InnerContract' at pc: '70' + stack backtrace: + 0: backtrace_panic::InnerContract::__wrapper__InnerContract__inner + at [..]/src/lib.cairo:34:9 + + error occurred in contract 'OuterContract' at pc: '107' + stack backtrace: + 0: backtrace_panic::IInnerContractDispatcherImpl::inner + at [..]/src/lib.cairo:22:1 + 1: backtrace_panic::OuterContract::OuterContract::outer + at [..]/src/lib.cairo:17:13 + 2: backtrace_panic::OuterContract::__wrapper__OuterContract__outer + at [..]/src/lib.cairo:15:9" + }, + ); +} diff --git a/crates/forge/tests/e2e/common/runner.rs b/crates/forge/tests/e2e/common/runner.rs index 9a8d05e789..72a76cd405 100644 --- a/crates/forge/tests/e2e/common/runner.rs +++ b/crates/forge/tests/e2e/common/runner.rs @@ -33,13 +33,33 @@ pub(crate) fn test_runner(temp_dir: &TempDir) -> SnapboxCommand { pub(crate) static BASE_FILE_PATTERNS: &[&str] = &["**/*.cairo", "**/*.toml"]; +fn is_package_from_docs_listings(package: &str) -> bool { + let package_path = Path::new("../../docs/listings").join(package); + fs::canonicalize(&package_path).is_ok() +} + pub(crate) fn setup_package_with_file_patterns( package_name: &str, file_patterns: &[&str], ) -> TempDir { let temp = tempdir_with_tool_versions().unwrap(); - temp.copy_from(format!("tests/data/{package_name}"), file_patterns) - .unwrap(); + + let is_from_docs_listings = is_package_from_docs_listings(package_name); + + let package_path = if is_from_docs_listings { + format!("../../docs/listings/{package_name}",) + } else { + format!("tests/data/{package_name}",) + }; + + let package_path = Utf8PathBuf::from_str(&package_path) + .unwrap() + .canonicalize_utf8() + .unwrap() + .to_string() + .replace('\\', "/"); + + temp.copy_from(package_path, file_patterns).unwrap(); let snforge_std_path = Utf8PathBuf::from_str("../../snforge_std") .unwrap() @@ -54,7 +74,15 @@ pub(crate) fn setup_package_with_file_patterns( .unwrap() .parse::() .unwrap(); - scarb_toml["dev-dependencies"]["snforge_std"]["path"] = value(snforge_std_path); + + let is_workspace = scarb_toml.get("workspace").is_some(); + + if is_workspace { + scarb_toml["workspace"]["dependencies"]["snforge_std"]["path"] = value(snforge_std_path); + } else { + scarb_toml["dev-dependencies"]["snforge_std"]["path"] = value(snforge_std_path); + } + scarb_toml["dependencies"]["starknet"] = value("2.4.0"); scarb_toml["dependencies"]["assert_macros"] = value(get_assert_macros_version().unwrap().to_string()); @@ -209,12 +237,7 @@ pub(crate) fn get_remote_url() -> String { .output_checked() .unwrap(); - String::from_utf8(output.stdout) - .unwrap() - .trim() - .strip_prefix("git@github.com:") - .unwrap() - .to_string() + String::from_utf8(output.stdout).unwrap().trim().to_string() } } diff --git a/crates/forge/tests/e2e/docs_snippets_validation.rs b/crates/forge/tests/e2e/docs_snippets_validation.rs new file mode 100644 index 0000000000..a385d1c06a --- /dev/null +++ b/crates/forge/tests/e2e/docs_snippets_validation.rs @@ -0,0 +1,59 @@ +use clap::Parser; +use docs::snippet::SnippetType; +use docs::utils::{ + assert_valid_snippet, get_nth_ancestor, print_skipped_snippet_message, print_success_message, +}; +use docs::validation::extract_snippets_from_directory; +use forge::Cli; +use shared::test_utils::output_assert::assert_stdout_contains; + +use super::common::runner::{runner, setup_package}; + +#[test] +fn test_docs_snippets() { + let root_dir = get_nth_ancestor(2); + let docs_dir = root_dir.join("docs/src"); + + let snippet_type = SnippetType::forge(); + + let snippets = extract_snippets_from_directory(&docs_dir, &snippet_type) + .expect("Failed to extract command snippets"); + + for snippet in &snippets { + let args = snippet.to_command_args(); + let mut args: Vec<&str> = args.iter().map(String::as_str).collect(); + + if snippet.config.ignored.unwrap_or(false) { + print_skipped_snippet_message(snippet); + continue; + } + + let parse_result = Cli::try_parse_from(args.clone()); + let err_message = if let Err(err) = &parse_result { + err.to_string() + } else { + String::new() + }; + + assert_valid_snippet(parse_result.is_ok(), snippet, &err_message); + + // Remove "snforge" from the args + args.remove(0); + + if let Some(snippet_output) = &snippet.output { + let package_name = snippet + .config + .package_name + .clone() + .or_else(|| snippet.capture_package_from_output()) + .expect("Cannot find package name in command output or snippet config"); + + let temp = setup_package(&package_name); + let output = runner(&temp).args(args).assert(); + + assert_stdout_contains(output, snippet_output); + } + } + + print_success_message(snippets.len(), snippet_type.as_str()); +} diff --git a/crates/forge/tests/e2e/mod.rs b/crates/forge/tests/e2e/mod.rs index f67f9a7544..9fbf0d711c 100644 --- a/crates/forge/tests/e2e/mod.rs +++ b/crates/forge/tests/e2e/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod common; +mod backtrace; mod build_profile; mod build_trace_data; mod collection; @@ -7,6 +8,7 @@ mod color; mod components; mod contract_artifacts; mod coverage; +mod docs_snippets_validation; mod env; mod features; mod fork_warning; diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index e884df8b3a..639d6439c4 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -63,7 +63,7 @@ pub struct StarknetRuntime<'a> { pub hint_handler: SyscallHintProcessor<'a>, } -impl<'a> SyscallPtrAccess for StarknetRuntime<'a> { +impl SyscallPtrAccess for StarknetRuntime<'_> { fn get_mut_syscall_ptr(&mut self) -> &mut Relocatable { &mut self.hint_handler.syscall_ptr } @@ -73,7 +73,7 @@ impl<'a> SyscallPtrAccess for StarknetRuntime<'a> { } } -impl<'a> ResourceTracker for StarknetRuntime<'a> { +impl ResourceTracker for StarknetRuntime<'_> { fn consumed(&self) -> bool { self.hint_handler.context.vm_run_resources.consumed() } @@ -91,7 +91,7 @@ impl<'a> ResourceTracker for StarknetRuntime<'a> { } } -impl<'a> SignalPropagator for StarknetRuntime<'a> { +impl SignalPropagator for StarknetRuntime<'_> { fn propagate_system_call_signal( &mut self, _selector: DeprecatedSyscallSelector, @@ -121,7 +121,7 @@ fn fetch_cheatcode_input( Ok(inputs) } -impl<'a> HintProcessorLogic for StarknetRuntime<'a> { +impl HintProcessorLogic for StarknetRuntime<'_> { fn execute_hint( &mut self, vm: &mut VirtualMachine, diff --git a/crates/scarb-api/src/artifacts.rs b/crates/scarb-api/src/artifacts.rs index e16a6d7178..b9b39ef358 100644 --- a/crates/scarb-api/src/artifacts.rs +++ b/crates/scarb-api/src/artifacts.rs @@ -61,7 +61,6 @@ impl StarknetArtifactsFiles { } } -// TODO(#2625) add unit tests fn unique_artifacts( artifact_representations: Vec, current_artifacts: &HashMap, diff --git a/crates/shared/src/consts.rs b/crates/shared/src/consts.rs index d94812861f..b39af175b0 100644 --- a/crates/shared/src/consts.rs +++ b/crates/shared/src/consts.rs @@ -1,3 +1,4 @@ pub const EXPECTED_RPC_VERSION: &str = "0.7.0"; pub const RPC_URL_VERSION: &str = "v0_7"; pub const SNFORGE_TEST_FILTER: &str = "SNFORGE_TEST_FILTER"; +pub const FREE_RPC_PROVIDER_URL: &str = "https://free-rpc.nethermind.io/sepolia-juno/v0_7"; diff --git a/crates/sncast/Cargo.toml b/crates/sncast/Cargo.toml index 142234b81a..3319c1a9ae 100644 --- a/crates/sncast/Cargo.toml +++ b/crates/sncast/Cargo.toml @@ -56,6 +56,7 @@ serde_path_to_error.workspace = true walkdir.workspace = true const-hex.workspace = true regex.workspace = true +dirs.workspace = true [dev-dependencies] ctor.workspace = true @@ -66,6 +67,7 @@ tempfile.workspace = true test-case.workspace = true fs_extra.workspace = true wiremock.workspace = true +docs = { workspace = true, features = ["testing"] } [[bin]] name = "sncast" diff --git a/crates/sncast/README.md b/crates/sncast/README.md index 67cae4aaf2..d7dda0a8cc 100644 --- a/crates/sncast/README.md +++ b/crates/sncast/README.md @@ -33,7 +33,6 @@ All subcommand usages are shown for two scenarios - when all necessary arguments ```shell $ sncast --account myuser \ - --url http://127.0.0.1:5050/rpc \ declare \ --contract-name SimpleBalance ``` @@ -72,8 +71,8 @@ transaction_hash: 0x7ad0d6e449e33b6581a4bb8df866c0fce3919a5ee05a30840ba521dafee2 ```shell $ sncast --account myuser \ + deploy --class-hash 0x8448a68b5ea1affc45e3fd4b8b480ea36a51dc34e337a16d2567d32d0c6f8a \ --url http://127.0.0.1:5050/rpc \ - deploy --class-hash 0x8448a68b5ea1affc45e3fd4b8b480ea36a51dc34e337a16d2567d32d0c6f8a ```
@@ -108,12 +107,12 @@ transaction_hash: 0x64a62a000240e034d1862c2bbfa154aac6a8195b4b2e570f38bf4fd47a5a ### Invoke a contract ```shell -$ sncast --url http://127.0.0.1:5050 \ +$ sncast \ --account example_user \ invoke \ --contract-address 0x4a739ab73aa3cac01f9da5d55f49fb67baee4919224454a2e3f85b16462a911 \ --function "some_function" \ - --calldata 1 2 3 + --arguments '1, 2, 3' ```
@@ -133,7 +132,7 @@ With arguments taken from `snfoundry.toml` file (default profile name): $ sncast invoke \ --contract-address 0x4a739ab73aa3cac01f9da5d55f49fb67baee4919224454a2e3f85b16462a911 \ --function "some_function" \ - --calldata 1 2 3 + --arguments '1, 2, 3' ```
@@ -149,11 +148,11 @@ transaction_hash: 0x7ad0d6e449e33b6581a4bb8df866c0fce3919a5ee05a30840ba521dafee2 ### Call a contract ```shell -$ sncast --url http://127.0.0.1:5050 \ +$ sncast \ call \ --contract-address 0x4a739ab73aa3cac01f9da5d55f49fb67baee4919224454a2e3f85b16462a911 \ --function "some_function" \ - --calldata 1 2 3 + --arguments '1, 2, 3' ```
@@ -172,8 +171,8 @@ With arguments taken from `snfoundry.toml` file (default profile name): ```shell $ sncast call \ --contract-address 0x4a739ab73aa3cac01f9da5d55f49fb67baee4919224454a2e3f85b16462a911 \ - --function some_function \ - --calldata 1 2 3 + --function "some_function" \ + --arguments '1, 2, 3' ```
diff --git a/crates/sncast/src/helpers/config.rs b/crates/sncast/src/helpers/config.rs new file mode 100644 index 0000000000..0ef8e9392a --- /dev/null +++ b/crates/sncast/src/helpers/config.rs @@ -0,0 +1,115 @@ +use crate::helpers::configuration::{show_explorer_links_default, CastConfig}; +use crate::helpers::constants::DEFAULT_ACCOUNTS_FILE; +use crate::ValidatedWaitParams; +use anyhow::Result; +use camino::Utf8PathBuf; +use indoc::formatdoc; +use shared::consts::FREE_RPC_PROVIDER_URL; +use std::fs; +use std::fs::File; +use std::io::Write; + +pub fn get_global_config_path() -> Result { + let global_config_dir = { + if cfg!(target_os = "windows") { + dirs::config_dir() + .ok_or_else(|| anyhow::anyhow!("Could not determine config directory"))? + .join("starknet-foundry") + } else { + dirs::home_dir() + .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))? + .join(".config/starknet-foundry") + } + }; + + if !global_config_dir.exists() { + fs::create_dir_all(&global_config_dir)?; + } + + let global_config_path = Utf8PathBuf::from_path_buf(global_config_dir.join("snfoundry.toml")) + .expect("Failed to convert PathBuf to Utf8PathBuf for global configuration"); + + if !global_config_path.exists() { + create_global_config(global_config_path.clone())?; + } + + Ok(global_config_path) +} + +fn build_default_manifest() -> String { + let default_wait_params = ValidatedWaitParams::default(); + + formatdoc! {r#" + # Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html + # and https://foundry-rs.github.io/starknet-foundry/projects/configuration.html for more information + + # [sncast.default] + # url = "{default_url}" + # block-explorer = "{default_block_explorer}" + # wait-params = {{ timeout = {default_wait_timeout}, retry-interval = {default_wait_retry_interval} }} + # show-explorer-links = {default_show_explorer_links} + # accounts-file = "{default_accounts_file}" + # account = "{default_account}" + # keystore = "{default_keystore}" + "#, + default_url = FREE_RPC_PROVIDER_URL, + default_accounts_file = DEFAULT_ACCOUNTS_FILE, + default_wait_timeout = default_wait_params.timeout, + default_wait_retry_interval = default_wait_params.retry_interval, + default_block_explorer = "StarkScan", + default_show_explorer_links = show_explorer_links_default(), + default_account = "default", + default_keystore = "" + } +} + +fn create_global_config(global_config_path: Utf8PathBuf) -> Result<()> { + let mut file = File::create(global_config_path)?; + file.write_all(build_default_manifest().as_bytes())?; + + Ok(()) +} +macro_rules! clone_field { + ($global_config:expr, $local_config:expr, $default_config:expr, $field:ident) => { + if $local_config.$field != $default_config.$field { + $local_config.$field.clone() + } else { + $global_config.$field.clone() + } + }; +} + +#[must_use] +pub fn combine_cast_configs(global_config: &CastConfig, local_config: &CastConfig) -> CastConfig { + let default_cast_config = CastConfig::default(); + + CastConfig { + url: clone_field!(global_config, local_config, default_cast_config, url), + account: clone_field!(global_config, local_config, default_cast_config, account), + accounts_file: clone_field!( + global_config, + local_config, + default_cast_config, + accounts_file + ), + keystore: clone_field!(global_config, local_config, default_cast_config, keystore), + wait_params: clone_field!( + global_config, + local_config, + default_cast_config, + wait_params + ), + block_explorer: clone_field!( + global_config, + local_config, + default_cast_config, + block_explorer + ), + show_explorer_links: clone_field!( + global_config, + local_config, + default_cast_config, + show_explorer_links + ), + } +} diff --git a/crates/sncast/src/helpers/configuration.rs b/crates/sncast/src/helpers/configuration.rs index c2603233bc..43d694b473 100644 --- a/crates/sncast/src/helpers/configuration.rs +++ b/crates/sncast/src/helpers/configuration.rs @@ -1,12 +1,12 @@ +use super::block_explorer; use crate::ValidatedWaitParams; use anyhow::Result; use camino::Utf8PathBuf; -use configuration::GlobalConfig; +use configuration::Config; use serde::{Deserialize, Serialize}; -use super::block_explorer; - -const fn show_explorer_links_default() -> bool { +#[must_use] +pub const fn show_explorer_links_default() -> bool { true } @@ -57,12 +57,12 @@ impl Default for CastConfig { keystore: None, wait_params: ValidatedWaitParams::default(), block_explorer: Some(block_explorer::Service::default()), - show_explorer_links: true, + show_explorer_links: show_explorer_links_default(), } } } -impl GlobalConfig for CastConfig { +impl Config for CastConfig { #[must_use] fn tool_name() -> &'static str { "sncast" diff --git a/crates/sncast/src/helpers/mod.rs b/crates/sncast/src/helpers/mod.rs index b9ac734f9e..c88c105765 100644 --- a/crates/sncast/src/helpers/mod.rs +++ b/crates/sncast/src/helpers/mod.rs @@ -1,5 +1,6 @@ pub mod block_explorer; pub mod braavos; +pub mod config; pub mod configuration; pub mod constants; pub mod error; diff --git a/crates/sncast/src/main.rs b/crates/sncast/src/main.rs index 84cd3d4c37..710f2a97bb 100644 --- a/crates/sncast/src/main.rs +++ b/crates/sncast/src/main.rs @@ -1,11 +1,8 @@ -use crate::starknet_commands::account::Account; -use crate::starknet_commands::show_config::ShowConfig; use crate::starknet_commands::{ - account, call::Call, declare::Declare, deploy::Deploy, invoke::Invoke, multicall::Multicall, - script::Script, tx_status::TxStatus, + account, account::Account, call::Call, declare::Declare, deploy::Deploy, invoke::Invoke, + multicall::Multicall, script::Script, show_config::ShowConfig, tx_status::TxStatus, }; use anyhow::{Context, Result}; -use configuration::load_global_config; use data_transformer::Calldata; use sncast::response::explorer_link::print_block_explorer_link_if_allowed; use sncast::response::print::{print_command_result, OutputFormat}; @@ -13,6 +10,8 @@ use sncast::response::print::{print_command_result, OutputFormat}; use crate::starknet_commands::deploy::DeployArguments; use camino::Utf8PathBuf; use clap::{Parser, Subcommand}; +use configuration::load_config; +use sncast::helpers::config::{combine_cast_configs, get_global_config_path}; use sncast::helpers::configuration::CastConfig; use sncast::helpers::constants::{DEFAULT_ACCOUNTS_FILE, DEFAULT_MULTICALL_CONTENTS}; use sncast::helpers::fee::PayableTransaction; @@ -209,8 +208,7 @@ fn main() -> Result<()> { if let Commands::Script(script) = &cli.command { run_script_command(&cli, runtime, script, numbers_format, output_format) } else { - let mut config = load_global_config::(&None, &cli.profile)?; - update_cast_config(&mut config, &cli); + let config = get_cast_config(&cli)?; runtime.block_on(run_async_command( cli, @@ -586,11 +584,15 @@ async fn run_async_command( }, Commands::ShowConfig(show) => { - let provider = show.rpc.get_provider(&config).await?; + let provider = show.rpc.get_provider(&config).await.ok(); - let result = - starknet_commands::show_config::show_config(&show, &provider, config, cli.profile) - .await; + let result = starknet_commands::show_config::show_config( + &show, + provider.as_ref(), + config, + cli.profile, + ) + .await; print_command_result("show-config", &result, numbers_format, output_format)?; @@ -657,11 +659,8 @@ fn run_script_command( let manifest_path = assert_manifest_path_exists()?; let package_metadata = get_package_metadata(&manifest_path, &run.package)?; - let mut config = load_global_config::( - &Some(package_metadata.root.clone()), - &cli.profile, - )?; - update_cast_config(&mut config, cli); + let config = get_cast_config(cli)?; + let provider = runtime.block_on(run.rpc.get_provider(&config))?; let mut artifacts = build_and_load_artifacts( @@ -715,7 +714,7 @@ fn run_script_command( Ok(()) } -fn update_cast_config(config: &mut CastConfig, cli: &Cli) { +fn config_with_cli(config: &mut CastConfig, cli: &Cli) { macro_rules! clone_or_else { ($field:expr, $config_field:expr) => { $field.clone().unwrap_or_else(|| $config_field.clone()) @@ -740,3 +739,23 @@ fn update_cast_config(config: &mut CastConfig, cli: &Cli) { clone_or_else!(cli.wait_timeout, config.wait_params.get_timeout()), ); } + +fn get_cast_config(cli: &Cli) -> Result { + let global_config_path = get_global_config_path().unwrap_or_else(|err| { + eprintln!("Error getting global config path: {err}"); + Utf8PathBuf::new() + }); + + let global_config = + load_config::(Some(&global_config_path.clone()), cli.profile.as_deref()) + .unwrap_or_else(|_| { + load_config::(Some(&global_config_path), None).unwrap() + }); + + let local_config = load_config::(None, cli.profile.as_deref())?; + + let mut combined_config = combine_cast_configs(&global_config, &local_config); + + config_with_cli(&mut combined_config, cli); + Ok(combined_config) +} diff --git a/crates/sncast/src/response/errors.rs b/crates/sncast/src/response/errors.rs index 5f40a35a95..8d62e8dfa3 100644 --- a/crates/sncast/src/response/errors.rs +++ b/crates/sncast/src/response/errors.rs @@ -4,9 +4,6 @@ use conversions::serde::serialize::CairoSerialize; use conversions::byte_array::ByteArray; -use starknet::core::types::StarknetError::{ - ContractError, TransactionExecutionError, ValidationFailure, -}; use starknet::core::types::{ContractErrorData, StarknetError, TransactionExecutionErrorData}; use starknet::providers::ProviderError; use thiserror::Error; @@ -111,15 +108,17 @@ impl From for SNCastStarknetError { StarknetError::InvalidTransactionIndex => SNCastStarknetError::InvalidTransactionIndex, StarknetError::ClassHashNotFound => SNCastStarknetError::ClassHashNotFound, StarknetError::TransactionHashNotFound => SNCastStarknetError::TransactionHashNotFound, - ContractError(err) => SNCastStarknetError::ContractError(err), - TransactionExecutionError(err) => SNCastStarknetError::TransactionExecutionError(err), + StarknetError::ContractError(err) => SNCastStarknetError::ContractError(err), + StarknetError::TransactionExecutionError(err) => { + SNCastStarknetError::TransactionExecutionError(err) + } StarknetError::ClassAlreadyDeclared => SNCastStarknetError::ClassAlreadyDeclared, StarknetError::InvalidTransactionNonce => SNCastStarknetError::InvalidTransactionNonce, StarknetError::InsufficientMaxFee => SNCastStarknetError::InsufficientMaxFee, StarknetError::InsufficientAccountBalance => { SNCastStarknetError::InsufficientAccountBalance } - ValidationFailure(err) => { + StarknetError::ValidationFailure(err) => { SNCastStarknetError::ValidationFailure(ByteArray::from(err.as_str())) } StarknetError::CompilationFailed => SNCastStarknetError::CompilationFailed, diff --git a/crates/sncast/src/response/print.rs b/crates/sncast/src/response/print.rs index 8128c8a6e4..6b4e49655a 100644 --- a/crates/sncast/src/response/print.rs +++ b/crates/sncast/src/response/print.rs @@ -75,6 +75,7 @@ impl From for OutputValue { .collect(), ), Value::String(s) => OutputValue::String(s.to_string()), + Value::Bool(b) => OutputValue::String(b.to_string()), s => panic!("{s:?} cannot be auto-serialized to output"), } } diff --git a/crates/sncast/src/response/structs.rs b/crates/sncast/src/response/structs.rs index affec75d54..33c4e15d52 100644 --- a/crates/sncast/src/response/structs.rs +++ b/crates/sncast/src/response/structs.rs @@ -1,4 +1,5 @@ use super::explorer_link::OutputLink; +use crate::helpers::block_explorer; use crate::helpers::block_explorer::LinkProvider; use camino::Utf8PathBuf; use conversions::padded_felt::PaddedFelt; @@ -107,13 +108,15 @@ impl CommandResponse for MulticallNewResponse {} #[derive(Serialize)] pub struct ShowConfigResponse { pub profile: Option, - pub chain_id: String, + pub chain_id: Option, pub rpc_url: Option, pub account: Option, pub accounts_file_path: Option, pub keystore: Option, pub wait_timeout: Option, pub wait_retry_interval: Option, + pub show_explorer_links: bool, + pub block_explorer: Option, } impl CommandResponse for ShowConfigResponse {} diff --git a/crates/sncast/src/starknet_commands/account/create.rs b/crates/sncast/src/starknet_commands/account/create.rs index 286ff21e4b..27d444e85f 100644 --- a/crates/sncast/src/starknet_commands/account/create.rs +++ b/crates/sncast/src/starknet_commands/account/create.rs @@ -116,7 +116,7 @@ pub async fn create( keystore, ..Default::default() }; - add_created_profile_to_configuration(&create.add_profile, &config, &None)?; + add_created_profile_to_configuration(create.add_profile.as_deref(), &config, None)?; } Ok(AccountCreateResponse { diff --git a/crates/sncast/src/starknet_commands/account/import.rs b/crates/sncast/src/starknet_commands/account/import.rs index 96d350055a..bd756b72d6 100644 --- a/crates/sncast/src/starknet_commands/account/import.rs +++ b/crates/sncast/src/starknet_commands/account/import.rs @@ -158,7 +158,7 @@ pub async fn import( accounts_file: accounts_file.into(), ..Default::default() }; - add_created_profile_to_configuration(&import.add_profile, &config, &None)?; + add_created_profile_to_configuration(import.add_profile.as_deref(), &config, None)?; } Ok(AccountImportResponse { diff --git a/crates/sncast/src/starknet_commands/account/list.rs b/crates/sncast/src/starknet_commands/account/list.rs index 0403064164..1e52c668ca 100644 --- a/crates/sncast/src/starknet_commands/account/list.rs +++ b/crates/sncast/src/starknet_commands/account/list.rs @@ -115,23 +115,23 @@ fn print_as_json(networks: &HashMap) -> anyho Ok(()) } -fn print_if_some(title: &str, item: &Option) { - if let Some(ref item) = item { +fn print_if_some(title: &str, item: Option<&T>) { + if let Some(item) = item { println!(" {title}: {item}"); } } fn print_pretty(data: &AccountDataRepresentation, name: &str) { println!("- {name}:"); - print_if_some("network", &data.network); - print_if_some("private key", &data.private_key); + print_if_some("network", data.network.as_ref()); + print_if_some("private key", data.private_key.as_ref()); println!(" public key: {}", data.public_key); - print_if_some("address", &data.address); - print_if_some("salt", &data.salt); - print_if_some("class hash", &data.class_hash); - print_if_some("deployed", &data.deployed); - print_if_some("legacy", &data.legacy); - print_if_some("type", &data.account_type); + print_if_some("address", data.address.as_ref()); + print_if_some("salt", data.salt.as_ref()); + print_if_some("class hash", data.class_hash.as_ref()); + print_if_some("deployed", data.deployed.as_ref()); + print_if_some("legacy", data.legacy.as_ref()); + print_if_some("type", data.account_type.as_ref()); println!(); } diff --git a/crates/sncast/src/starknet_commands/account/mod.rs b/crates/sncast/src/starknet_commands/account/mod.rs index d507a39317..b636757aa4 100644 --- a/crates/sncast/src/starknet_commands/account/mod.rs +++ b/crates/sncast/src/starknet_commands/account/mod.rs @@ -7,7 +7,7 @@ use anyhow::{anyhow, bail, Context, Result}; use camino::Utf8PathBuf; use clap::{Args, Subcommand, ValueEnum}; use configuration::{ - find_config_file, load_global_config, search_config_upwards_relative_to, CONFIG_FILENAME, + find_config_file, load_config, search_config_upwards_relative_to, CONFIG_FILENAME, }; use serde_json::json; use sncast::{chain_id_to_network_name, decode_chain_id, helpers::configuration::CastConfig}; @@ -124,18 +124,18 @@ pub fn write_account_to_accounts_file( } pub fn add_created_profile_to_configuration( - profile: &Option, + profile: Option<&str>, cast_config: &CastConfig, - path: &Option, + path: Option<&Utf8PathBuf>, ) -> Result<()> { - if !load_global_config::(path, profile) + if !load_config::(path, profile) .unwrap_or_default() .account .is_empty() { bail!( "Failed to add profile = {} to the snfoundry.toml. Profile already exists", - profile.as_ref().unwrap_or(&"default".to_string()) + profile.unwrap_or("default") ); } @@ -157,9 +157,7 @@ pub fn add_created_profile_to_configuration( } let mut profile_config = toml::value::Table::new(); profile_config.insert( - profile - .clone() - .unwrap_or_else(|| cast_config.account.clone()), + profile.map_or_else(|| cast_config.account.clone(), ToString::to_string), Value::Table(new_profile), ); @@ -208,9 +206,9 @@ mod tests { ..Default::default() }; let res = add_created_profile_to_configuration( - &Some(String::from("some-name")), + Some(&String::from("some-name")), &config, - &Some(path.clone()), + Some(&path.clone()), ); assert!(res.is_ok()); @@ -233,9 +231,9 @@ mod tests { ..Default::default() }; let res = add_created_profile_to_configuration( - &Some(String::from("default")), + Some(&String::from("default")), &config, - &Some(Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), + Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()), ); assert!(res.is_err()); } diff --git a/crates/sncast/src/starknet_commands/declare.rs b/crates/sncast/src/starknet_commands/declare.rs index 0dffb9ed76..6cdbe8a406 100644 --- a/crates/sncast/src/starknet_commands/declare.rs +++ b/crates/sncast/src/starknet_commands/declare.rs @@ -150,6 +150,6 @@ pub async fn declare( })) } Err(Provider(error)) => Err(StarknetCommandError::ProviderError(error.into())), - _ => Err(anyhow!("Unknown RPC error").into()), + Err(error) => Err(anyhow!(format!("Unexpected error occurred: {error}")).into()), } } diff --git a/crates/sncast/src/starknet_commands/deploy.rs b/crates/sncast/src/starknet_commands/deploy.rs index 2cbf89db28..8b3bcb4e29 100644 --- a/crates/sncast/src/starknet_commands/deploy.rs +++ b/crates/sncast/src/starknet_commands/deploy.rs @@ -140,6 +140,6 @@ pub async fn deploy( .await .map_err(StarknetCommandError::from), Err(Provider(error)) => Err(StarknetCommandError::ProviderError(error.into())), - _ => Err(anyhow!("Unknown RPC error").into()), + Err(error) => Err(anyhow!(format!("Unexpected error occurred: {error}")).into()), } } diff --git a/crates/sncast/src/starknet_commands/invoke.rs b/crates/sncast/src/starknet_commands/invoke.rs index 636f68941d..8d481d6cf8 100644 --- a/crates/sncast/src/starknet_commands/invoke.rs +++ b/crates/sncast/src/starknet_commands/invoke.rs @@ -118,6 +118,6 @@ pub async fn execute_calls( .await .map_err(StarknetCommandError::from), Err(Provider(error)) => Err(StarknetCommandError::ProviderError(error.into())), - _ => Err(anyhow!("Unknown RPC error").into()), + Err(error) => Err(anyhow!(format!("Unexpected error occurred: {error}")).into()), } } diff --git a/crates/sncast/src/starknet_commands/script/run.rs b/crates/sncast/src/starknet_commands/script/run.rs index 03e3617203..64de368a37 100644 --- a/crates/sncast/src/starknet_commands/script/run.rs +++ b/crates/sncast/src/starknet_commands/script/run.rs @@ -81,7 +81,7 @@ pub struct CastScriptExtension<'a> { pub state: StateManager, } -impl<'a> CastScriptExtension<'a> { +impl CastScriptExtension<'_> { pub fn account( &self, ) -> Result<&SingleOwnerAccount<&JsonRpcClient, LocalWallet>> { diff --git a/crates/sncast/src/starknet_commands/show_config.rs b/crates/sncast/src/starknet_commands/show_config.rs index ba61c7dc8e..4cfbbcaf2e 100644 --- a/crates/sncast/src/starknet_commands/show_config.rs +++ b/crates/sncast/src/starknet_commands/show_config.rs @@ -18,12 +18,17 @@ pub struct ShowConfig { #[allow(clippy::ptr_arg)] pub async fn show_config( show: &ShowConfig, - provider: &JsonRpcClient, + provider: Option<&JsonRpcClient>, cast_config: CastConfig, profile: Option, ) -> Result { - let chain_id_field = get_chain_id(provider).await?; - let chain_id = chain_id_to_network_name(chain_id_field); + let chain_id = if let Some(provider) = provider { + let chain_id_field = get_chain_id(provider).await?; + Some(chain_id_to_network_name(chain_id_field)) + } else { + None + }; + let rpc_url = Some(show.rpc.url.clone().unwrap_or(cast_config.url)).filter(|p| !p.is_empty()); let account = Some(cast_config.account).filter(|p| !p.is_empty()); let mut accounts_file_path = @@ -34,6 +39,7 @@ pub async fn show_config( } let wait_timeout = Some(cast_config.wait_params.get_timeout()); let wait_retry_interval = Some(cast_config.wait_params.get_retry_interval()); + let block_explorer = cast_config.block_explorer; Ok(ShowConfigResponse { profile, @@ -44,5 +50,7 @@ pub async fn show_config( keystore, wait_timeout: wait_timeout.map(|x| Decimal(u64::from(x))), wait_retry_interval: wait_retry_interval.map(|x| Decimal(u64::from(x))), + show_explorer_links: cast_config.show_explorer_links, + block_explorer, }) } diff --git a/crates/sncast/tests/data/files/correct_snfoundry.toml b/crates/sncast/tests/data/files/correct_snfoundry.toml index 83a09dbf38..0baa551e1b 100644 --- a/crates/sncast/tests/data/files/correct_snfoundry.toml +++ b/crates/sncast/tests/data/files/correct_snfoundry.toml @@ -25,3 +25,9 @@ account = "user3" [sncast.profile5] url = "http://127.0.0.1:5055/rpc" account = "user8" + +[sncast.profile6] +accounts-file = "/path/to/account.json" +account = "user1" +wait-params = { timeout = 500, retry-interval = 10 } +show-explorer-links = false diff --git a/crates/sncast/tests/docs_snippets/mod.rs b/crates/sncast/tests/docs_snippets/mod.rs new file mode 100644 index 0000000000..8695201df0 --- /dev/null +++ b/crates/sncast/tests/docs_snippets/mod.rs @@ -0,0 +1 @@ +pub mod validation; diff --git a/crates/sncast/tests/docs_snippets/validation.rs b/crates/sncast/tests/docs_snippets/validation.rs new file mode 100644 index 0000000000..362cd07e7e --- /dev/null +++ b/crates/sncast/tests/docs_snippets/validation.rs @@ -0,0 +1,52 @@ +use docs::snippet::{Snippet, SnippetType}; +use docs::utils::{ + assert_valid_snippet, get_nth_ancestor, print_skipped_snippet_message, print_success_message, +}; +use docs::validation::{extract_snippets_from_directory, extract_snippets_from_file}; +use tempfile::tempdir; + +use crate::helpers::runner::runner; + +#[test] +fn test_docs_snippets() { + let tempdir = tempdir().expect("Unable to create a temporary directory"); + + let root_dir_path = get_nth_ancestor(2); + let docs_dir_path = root_dir_path.join("docs/src"); + let sncast_readme_path = root_dir_path.join("crates/sncast/README.md"); + + let snippet_type = SnippetType::sncast(); + + let docs_snippets = extract_snippets_from_directory(&docs_dir_path, &snippet_type) + .expect("Failed to extract command snippets"); + + let readme_snippets = extract_snippets_from_file(&sncast_readme_path, &snippet_type) + .expect("Failed to extract command snippets"); + + let snippets = docs_snippets + .into_iter() + .chain(readme_snippets) + .collect::>(); + + for snippet in &snippets { + let args = snippet.to_command_args(); + let mut args: Vec<&str> = args.iter().map(String::as_str).collect(); + + // remove "sncast" from the args + args.remove(0); + + if snippet.config.ignored.unwrap_or(false) { + print_skipped_snippet_message(snippet); + continue; + } + + let snapbox = runner(&args).current_dir(tempdir.path()); + let output = snapbox.output().expect("Failed to execute the command"); + let exit_code = output.status.code().unwrap_or_default(); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_valid_snippet(exit_code != 2, snippet, &stderr); + } + + print_success_message(snippets.len(), snippet_type.as_str()); +} diff --git a/crates/sncast/tests/e2e/main_tests.rs b/crates/sncast/tests/e2e/main_tests.rs index 960bac8a63..c52510cb1d 100644 --- a/crates/sncast/tests/e2e/main_tests.rs +++ b/crates/sncast/tests/e2e/main_tests.rs @@ -169,29 +169,6 @@ async fn test_missing_account_flag() { ); } -#[tokio::test] -async fn test_missing_url() { - let args = vec![ - "--accounts-file", - ACCOUNT_FILE_PATH, - "--account", - ACCOUNT, - "declare", - "--contract-name", - "whatever", - "--fee-token", - "eth", - ]; - - let snapbox = runner(&args); - let output = snapbox.assert().failure(); - - assert_stderr_contains( - output, - "Error: RPC url not passed nor found in snfoundry.toml", - ); -} - #[tokio::test] async fn test_inexistent_keystore() { let args = vec![ diff --git a/crates/sncast/tests/e2e/script/general.rs b/crates/sncast/tests/e2e/script/general.rs index 5b3f0727d0..5b82923645 100644 --- a/crates/sncast/tests/e2e/script/general.rs +++ b/crates/sncast/tests/e2e/script/general.rs @@ -135,7 +135,7 @@ async fn test_incompatible_sncast_std_version() { snapbox.assert().success().stdout_matches(indoc! {r" ... - [WARNING] Package sncast_std version does not meet the recommended version requirement =0.33.0, it might result in unexpected behaviour + [WARNING] Package sncast_std version does not meet the recommended version requirement =0.34.0, it might result in unexpected behaviour ... "}); } diff --git a/crates/sncast/tests/e2e/show_config.rs b/crates/sncast/tests/e2e/show_config.rs index 57f35cb569..ae1dd14629 100644 --- a/crates/sncast/tests/e2e/show_config.rs +++ b/crates/sncast/tests/e2e/show_config.rs @@ -15,6 +15,7 @@ async fn test_show_config_from_snfoundry_toml() { accounts_file_path: ../account-file chain_id: alpha-sepolia rpc_url: {} + show_explorer_links: true wait_retry_interval: 5 wait_timeout: 300 ", URL}); @@ -44,6 +45,7 @@ async fn test_show_config_from_cli() { chain_id: alpha-sepolia keystore: ../keystore rpc_url: {} + show_explorer_links: true wait_retry_interval: 1 wait_timeout: 2 ", URL}); @@ -63,6 +65,7 @@ async fn test_show_config_from_cli_and_snfoundry_toml() { chain_id: alpha-sepolia profile: profile2 rpc_url: {} + show_explorer_links: true wait_retry_interval: 5 wait_timeout: 300 ", URL}); @@ -82,6 +85,7 @@ async fn test_show_config_when_no_keystore() { chain_id: alpha-sepolia profile: profile4 rpc_url: {} + show_explorer_links: true wait_retry_interval: 5 wait_timeout: 300 ", URL}); @@ -101,7 +105,26 @@ async fn test_show_config_when_keystore() { keystore: ../keystore profile: profile3 rpc_url: {} + show_explorer_links: true wait_retry_interval: 5 wait_timeout: 300 ", URL}); } + +#[tokio::test] +async fn test_show_config_no_url() { + let tempdir = copy_config_to_tempdir("tests/data/files/correct_snfoundry.toml", None).unwrap(); + let args = vec!["--profile", "profile6", "show-config"]; + + let snapbox = runner(&args).current_dir(tempdir.path()); + + snapbox.assert().success().stdout_eq(formatdoc! {r" + command: show-config + account: user1 + accounts_file_path: /path/to/account.json + profile: profile6 + show_explorer_links: false + wait_retry_interval: 10 + wait_timeout: 500 + "}); +} diff --git a/crates/sncast/tests/helpers/devnet.rs b/crates/sncast/tests/helpers/devnet.rs index 5dc6cc7ab3..491f0634fb 100644 --- a/crates/sncast/tests/helpers/devnet.rs +++ b/crates/sncast/tests/helpers/devnet.rs @@ -11,6 +11,7 @@ use std::time::{Duration, Instant}; use tokio::runtime::Runtime; use url::Url; +#[allow(clippy::zombie_processes)] #[cfg(test)] #[ctor] fn start_devnet() { @@ -75,6 +76,7 @@ fn start_devnet() { rt.block_on(deploy_braavos_account()); } +#[allow(clippy::zombie_processes)] #[cfg(test)] #[dtor] fn stop_devnet() { diff --git a/crates/sncast/tests/main.rs b/crates/sncast/tests/main.rs index 5465fce9b8..255cc634cd 100644 --- a/crates/sncast/tests/main.rs +++ b/crates/sncast/tests/main.rs @@ -1,3 +1,4 @@ +mod docs_snippets; mod e2e; pub mod helpers; mod integration; diff --git a/crates/snforge-scarb-plugin/Scarb.toml b/crates/snforge-scarb-plugin/Scarb.toml index 3fceabef79..64519161c6 100644 --- a/crates/snforge-scarb-plugin/Scarb.toml +++ b/crates/snforge-scarb-plugin/Scarb.toml @@ -1,6 +1,6 @@ [package] name = "snforge_scarb_plugin" -version = "0.33.1" +version = "0.34.0" edition = "2024_07" [cairo-plugin] diff --git a/crates/snforge-scarb-plugin/src/args/unnamed.rs b/crates/snforge-scarb-plugin/src/args/unnamed.rs index 26dbdc2f44..79d6ef69d5 100644 --- a/crates/snforge-scarb-plugin/src/args/unnamed.rs +++ b/crates/snforge-scarb-plugin/src/args/unnamed.rs @@ -31,6 +31,6 @@ impl<'a> UnnamedArgs<'a> { ) -> Result<&[(usize, &'a Expr); N], Diagnostic> { self.as_slice() .try_into() - .map_err(|_| T::error(format!("expected {} arguments, got: {}", N, self.len()))) + .map_err(|_| T::error(format!("expected arguments: {}, got: {}", N, self.len()))) } } diff --git a/crates/snforge-scarb-plugin/src/common.rs b/crates/snforge-scarb-plugin/src/common.rs index 54c05dc53d..1f295d6985 100644 --- a/crates/snforge-scarb-plugin/src/common.rs +++ b/crates/snforge-scarb-plugin/src/common.rs @@ -18,10 +18,10 @@ pub fn into_proc_macro_result( match handler(&args, &item, &mut warns) { Ok(item) => ProcMacroResult::new(TokenStream::new(item)).with_diagnostics(warns.into()), - Err(diagnostics) => ProcMacroResult::new(item).with_diagnostics( - //TODO(#2358) extend with warns - diagnostics, - ), + Err(mut diagnostics) => { + diagnostics.extend(warns); + ProcMacroResult::new(item).with_diagnostics(diagnostics) + } } } diff --git a/crates/snforge-scarb-plugin/tests/integration/single_attributes/available_gas.rs b/crates/snforge-scarb-plugin/tests/integration/single_attributes/available_gas.rs index 8f2c978de0..890758c69c 100644 --- a/crates/snforge-scarb-plugin/tests/integration/single_attributes/available_gas.rs +++ b/crates/snforge-scarb-plugin/tests/integration/single_attributes/available_gas.rs @@ -12,8 +12,10 @@ fn fails_with_empty() { assert_diagnostics( &result, - &[Diagnostic::error( - "#[available_gas] expected 1 arguments, got: 0", + &[ + Diagnostic::warn("#[available_gas] used with empty argument list. Either remove () or specify some arguments"), + Diagnostic::error( + "#[available_gas] expected arguments: 1, got: 0", )], ); } @@ -28,7 +30,7 @@ fn fails_with_more_than_one() { assert_diagnostics( &result, &[Diagnostic::error( - "#[available_gas] expected 1 arguments, got: 3", + "#[available_gas] expected arguments: 1, got: 3", )], ); } diff --git a/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs b/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs index a92a4a02b4..efdf280d54 100644 --- a/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs +++ b/crates/snforge-scarb-plugin/tests/integration/single_attributes/fork.rs @@ -17,7 +17,7 @@ fn fails_without_block() { " All options failed - variant: exactly one of | | should be specified, got 0 - - variant: #[fork] expected 1 arguments, got: 0 + - variant: #[fork] expected arguments: 1, got: 0 - variant: #[fork] can be used with unnamed attributes only Resolve at least one of them " @@ -39,7 +39,7 @@ fn fails_without_url() { " All options failed - variant: argument is missing - - variant: #[fork] expected 1 arguments, got: 0 + - variant: #[fork] expected arguments: 1, got: 0 - variant: #[fork] can be used with unnamed attributes only Resolve at least one of them " @@ -56,12 +56,13 @@ fn fails_without_args() { assert_diagnostics( &result, - &[Diagnostic::error(formatdoc!( + &[Diagnostic::warn("#[fork] used with empty argument list. Either remove () or specify some arguments"), + Diagnostic::error(formatdoc!( " All options failed - variant: exactly one of | | should be specified, got 0 - - variant: #[fork] expected 1 arguments, got: 0 - - variant: #[fork] expected 1 arguments, got: 0 + - variant: #[fork] expected arguments: 1, got: 0 + - variant: #[fork] expected arguments: 1, got: 0 Resolve at least one of them " ))], @@ -81,7 +82,7 @@ fn fails_with_invalid_url() { " All options failed - variant: #[fork] is not a valid url - - variant: #[fork] expected 1 arguments, got: 0 + - variant: #[fork] expected arguments: 1, got: 0 - variant: #[fork] can be used with unnamed attributes only Resolve at least one of them " diff --git a/docs/README.md b/docs/README.md index 39b0255d33..f70abc808f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,11 +11,11 @@ $ cargo install mdbook ## Building ```shell -mdbook build +$ mdbook build ``` ## Open preview and reload on every change ```shell -mdbook serve +$ mdbook serve ``` \ No newline at end of file diff --git a/docs/listings/sncast_overview/scripts/basic_example/Scarb.toml b/docs/listings/basic_example/Scarb.toml similarity index 69% rename from docs/listings/sncast_overview/scripts/basic_example/Scarb.toml rename to docs/listings/basic_example/Scarb.toml index b425f3ed72..c637765bb1 100644 --- a/docs/listings/sncast_overview/scripts/basic_example/Scarb.toml +++ b/docs/listings/basic_example/Scarb.toml @@ -6,9 +6,9 @@ edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] -snforge_std.workspace = true -sncast_std.workspace = true -starknet.workspace = true +starknet = "2.7.0" +snforge_std = { path = "../../../snforge_std" } +sncast_std = { path = "../../../sncast_std" } [[target.lib]] sierra = true diff --git a/docs/listings/sncast_overview/scripts/basic_example/src/basic_example.cairo b/docs/listings/basic_example/src/basic_example.cairo similarity index 100% rename from docs/listings/sncast_overview/scripts/basic_example/src/basic_example.cairo rename to docs/listings/basic_example/src/basic_example.cairo diff --git a/docs/listings/sncast_overview/scripts/basic_example/src/lib.cairo b/docs/listings/basic_example/src/lib.cairo similarity index 100% rename from docs/listings/sncast_overview/scripts/basic_example/src/lib.cairo rename to docs/listings/basic_example/src/lib.cairo diff --git a/docs/listings/sncast_library/scripts/call/Scarb.toml b/docs/listings/call/Scarb.toml similarity index 55% rename from docs/listings/sncast_library/scripts/call/Scarb.toml rename to docs/listings/call/Scarb.toml index 445f83833b..d3a1355702 100644 --- a/docs/listings/sncast_library/scripts/call/Scarb.toml +++ b/docs/listings/call/Scarb.toml @@ -4,8 +4,9 @@ version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -sncast_std.workspace = true +starknet = "2.7.0" +sncast_std = { path = "../../../sncast_std" } +snforge_std = { path = "../../../snforge_std" } [[target.lib]] sierra = true diff --git a/docs/listings/sncast_library/scripts/call/src/lib.cairo b/docs/listings/call/src/lib.cairo similarity index 100% rename from docs/listings/sncast_library/scripts/call/src/lib.cairo rename to docs/listings/call/src/lib.cairo diff --git a/docs/listings/snforge_advanced_features/crates/conditional_compilation/Scarb.toml b/docs/listings/conditional_compilation/Scarb.toml similarity index 68% rename from docs/listings/snforge_advanced_features/crates/conditional_compilation/Scarb.toml rename to docs/listings/conditional_compilation/Scarb.toml index 598a980be5..136ac8da96 100644 --- a/docs/listings/snforge_advanced_features/crates/conditional_compilation/Scarb.toml +++ b/docs/listings/conditional_compilation/Scarb.toml @@ -8,9 +8,11 @@ default = ["enable_for_tests"] enable_for_tests = [] [dependencies] -starknet.workspace = true -snforge_std.workspace = true -assert_macros.workspace = true +starknet = "2.7.0" +assert_macros = "0.1.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } [[target.starknet-contract]] sierra = true diff --git a/docs/listings/snforge_advanced_features/crates/conditional_compilation/src/contract.cairo b/docs/listings/conditional_compilation/src/contract.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/conditional_compilation/src/contract.cairo rename to docs/listings/conditional_compilation/src/contract.cairo diff --git a/docs/listings/snforge_advanced_features/crates/conditional_compilation/src/function.cairo b/docs/listings/conditional_compilation/src/function.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/conditional_compilation/src/function.cairo rename to docs/listings/conditional_compilation/src/function.cairo diff --git a/docs/listings/snforge_advanced_features/crates/conditional_compilation/src/lib.cairo b/docs/listings/conditional_compilation/src/lib.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/conditional_compilation/src/lib.cairo rename to docs/listings/conditional_compilation/src/lib.cairo diff --git a/docs/listings/snforge_advanced_features/crates/conditional_compilation/tests/test.cairo b/docs/listings/conditional_compilation/tests/test.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/conditional_compilation/tests/test.cairo rename to docs/listings/conditional_compilation/tests/test.cairo diff --git a/docs/listings/sncast_overview/Scarb.toml b/docs/listings/declare/Scarb.toml similarity index 66% rename from docs/listings/sncast_overview/Scarb.toml rename to docs/listings/declare/Scarb.toml index 4e682fa59d..caba58eb23 100644 --- a/docs/listings/sncast_overview/Scarb.toml +++ b/docs/listings/declare/Scarb.toml @@ -1,18 +1,18 @@ -[workspace] -members = ["scripts/*", "crates/*"] +[package] +name = "declare" +version = "0.1.0" +edition = "2023_11" -[workspace.dependencies] +[dependencies] starknet = "2.7.0" -snforge_std = { path = "../../../snforge_std" } sncast_std = { path = "../../../sncast_std" } -assert_macros = "0.1.0" +snforge_std = { path = "../../../snforge_std" } [[target.starknet-contract]] sierra = true [[target.lib]] sierra = true -casm = true [scripts] test = "snforge test" diff --git a/docs/listings/sncast_library/scripts/declare/src/lib.cairo b/docs/listings/declare/src/lib.cairo similarity index 100% rename from docs/listings/sncast_library/scripts/declare/src/lib.cairo rename to docs/listings/declare/src/lib.cairo diff --git a/docs/listings/sncast_library/scripts/deploy/Scarb.toml b/docs/listings/deploy/Scarb.toml similarity index 51% rename from docs/listings/sncast_library/scripts/deploy/Scarb.toml rename to docs/listings/deploy/Scarb.toml index 1ae2f99497..0a87b3ab07 100644 --- a/docs/listings/sncast_library/scripts/deploy/Scarb.toml +++ b/docs/listings/deploy/Scarb.toml @@ -4,8 +4,11 @@ version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -sncast_std.workspace = true +starknet = "2.7.0" +sncast_std = { path = "../../../sncast_std" } + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } [[target.lib]] sierra = true diff --git a/docs/listings/sncast_library/scripts/deploy/src/lib.cairo b/docs/listings/deploy/src/lib.cairo similarity index 100% rename from docs/listings/sncast_library/scripts/deploy/src/lib.cairo rename to docs/listings/deploy/src/lib.cairo diff --git a/docs/listings/detailed_resources_example/Scarb.toml b/docs/listings/detailed_resources_example/Scarb.toml new file mode 100644 index 0000000000..788a68ee9d --- /dev/null +++ b/docs/listings/detailed_resources_example/Scarb.toml @@ -0,0 +1,18 @@ +[package] +name = "detailed_resources_example" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/detailed_resources_example/src/lib.cairo b/docs/listings/detailed_resources_example/src/lib.cairo new file mode 100644 index 0000000000..4955786578 --- /dev/null +++ b/docs/listings/detailed_resources_example/src/lib.cairo @@ -0,0 +1,25 @@ +#[starknet::interface] +pub trait IHelloStarknet { + fn increase_balance(ref self: TContractState, amount: felt252); + fn get_balance(self: @TContractState) -> felt252; +} + +#[starknet::contract] +mod HelloStarknet { + #[storage] + struct Storage { + balance: felt252, + } + + #[abi(embed_v0)] + impl HelloStarknetImpl of super::IHelloStarknet { + fn increase_balance(ref self: ContractState, amount: felt252) { + assert(amount != 0, 'Amount cannot be 0'); + self.balance.write(self.balance.read() + amount); + } + + fn get_balance(self: @ContractState) -> felt252 { + self.balance.read() + } + } +} diff --git a/docs/listings/detailed_resources_example/tests/test_contract.cairo b/docs/listings/detailed_resources_example/tests/test_contract.cairo new file mode 100644 index 0000000000..42968bcd13 --- /dev/null +++ b/docs/listings/detailed_resources_example/tests/test_contract.cairo @@ -0,0 +1,15 @@ +#[test] +fn test_abc() { + assert(1 == 1, 1); +} + +#[test] +fn test_failing() { + assert(1 == 2, 'failing check'); +} + +#[test] +fn test_xyz() { + assert(1 == 1, 1); +} + diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/Scarb.toml b/docs/listings/direct_storage_access/Scarb.toml similarity index 60% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/Scarb.toml rename to docs/listings/direct_storage_access/Scarb.toml index 118608e7cb..b1f73067fa 100644 --- a/docs/listings/snforge_advanced_features/crates/direct_storage_access/Scarb.toml +++ b/docs/listings/direct_storage_access/Scarb.toml @@ -4,9 +4,11 @@ version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -snforge_std.workspace = true -assert_macros.workspace = true +starknet = "2.7.0" +assert_macros = "0.1.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } [[target.starknet-contract]] sierra = true diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/src/complex_structures.cairo b/docs/listings/direct_storage_access/src/complex_structures.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/src/complex_structures.cairo rename to docs/listings/direct_storage_access/src/complex_structures.cairo diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/src/felts_only.cairo b/docs/listings/direct_storage_access/src/felts_only.cairo similarity index 58% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/src/felts_only.cairo rename to docs/listings/direct_storage_access/src/felts_only.cairo index 82ad3d9511..9d0104e863 100644 --- a/docs/listings/snforge_advanced_features/crates/direct_storage_access/src/felts_only.cairo +++ b/docs/listings/direct_storage_access/src/felts_only.cairo @@ -1,8 +1,10 @@ #[starknet::interface] -pub trait ISimpleStorageContract {} +pub trait ISimpleStorageContract { + fn get_value(self: @TState, key: felt252) -> felt252; +} #[starknet::contract] -mod SimpleStorageContract { +pub mod SimpleStorageContract { use starknet::storage::Map; #[storage] @@ -16,4 +18,9 @@ mod SimpleStorageContract { self.plain_felt.write(0x2137_felt252); self.mapping.write('some_key', 'some_value'); } + + #[external(v0)] + pub fn get_value(self: @ContractState, key: felt252) -> felt252 { + self.mapping.read(key) + } } diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/src/lib.cairo b/docs/listings/direct_storage_access/src/lib.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/src/lib.cairo rename to docs/listings/direct_storage_access/src/lib.cairo diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/complex_structures.cairo b/docs/listings/direct_storage_access/tests/complex_structures.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/complex_structures.cairo rename to docs/listings/direct_storage_access/tests/complex_structures.cairo diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/felts_only.cairo b/docs/listings/direct_storage_access/tests/felts_only.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/felts_only.cairo rename to docs/listings/direct_storage_access/tests/felts_only.cairo diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/felts_only/field.cairo b/docs/listings/direct_storage_access/tests/felts_only/field.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/felts_only/field.cairo rename to docs/listings/direct_storage_access/tests/felts_only/field.cairo diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/felts_only/map_entry.cairo b/docs/listings/direct_storage_access/tests/felts_only/map_entry.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/felts_only/map_entry.cairo rename to docs/listings/direct_storage_access/tests/felts_only/map_entry.cairo diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/lib.cairo b/docs/listings/direct_storage_access/tests/lib.cairo similarity index 64% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/lib.cairo rename to docs/listings/direct_storage_access/tests/lib.cairo index 628d3a5239..f667aaf500 100644 --- a/docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/lib.cairo +++ b/docs/listings/direct_storage_access/tests/lib.cairo @@ -1,3 +1,4 @@ pub mod felts_only; pub mod complex_structures; pub mod storage_address; +pub mod using_storage_address_from_base; diff --git a/docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/storage_address.cairo b/docs/listings/direct_storage_access/tests/storage_address.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/direct_storage_access/tests/storage_address.cairo rename to docs/listings/direct_storage_access/tests/storage_address.cairo diff --git a/docs/listings/direct_storage_access/tests/using_storage_address_from_base.cairo b/docs/listings/direct_storage_access/tests/using_storage_address_from_base.cairo new file mode 100644 index 0000000000..8d8e26576b --- /dev/null +++ b/docs/listings/direct_storage_access/tests/using_storage_address_from_base.cairo @@ -0,0 +1,31 @@ +use starknet::storage::StorageAsPointer; +use starknet::storage::StoragePathEntry; + +use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, store, load}; +use starknet::storage_access::{storage_address_from_base}; + +use direct_storage_access::felts_only::{ + SimpleStorageContract, ISimpleStorageContractDispatcher, ISimpleStorageContractDispatcherTrait +}; + +#[test] +fn update_mapping() { + let key = 0; + let data = 100; + let (contract_address, _) = declare("SimpleStorageContract") + .unwrap() + .contract_class() + .deploy(@array![]) + .unwrap(); + let dispatcher = ISimpleStorageContractDispatcher { contract_address }; + let mut state = SimpleStorageContract::contract_state_for_testing(); + + let storage_address = storage_address_from_base( + state.mapping.entry(key).as_ptr().__storage_pointer_address__.into() + ); + let storage_value: Span = array![data.into()].span(); + store(contract_address, storage_address.into(), storage_value); + + let read_data = dispatcher.get_value(key.into()); + assert_eq!(read_data, data, "Storage update failed") +} diff --git a/docs/listings/sncast_overview/scripts/error_handling/Scarb.toml b/docs/listings/error_handling/Scarb.toml similarity index 65% rename from docs/listings/sncast_overview/scripts/error_handling/Scarb.toml rename to docs/listings/error_handling/Scarb.toml index b52b5904ba..5ef420704b 100644 --- a/docs/listings/sncast_overview/scripts/error_handling/Scarb.toml +++ b/docs/listings/error_handling/Scarb.toml @@ -6,10 +6,10 @@ edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] -snforge_std.workspace = true -sncast_std.workspace = true -starknet.workspace = true -assert_macros.workspace = true +snforge_std = { path = "../../../snforge_std" } +sncast_std = { path = "../../../sncast_std" } +starknet = "2.7.0" +assert_macros = "0.1.0" [[target.lib]] sierra = true diff --git a/docs/listings/sncast_overview/scripts/error_handling/src/error_handling.cairo b/docs/listings/error_handling/src/error_handling.cairo similarity index 100% rename from docs/listings/sncast_overview/scripts/error_handling/src/error_handling.cairo rename to docs/listings/error_handling/src/error_handling.cairo diff --git a/docs/listings/sncast_overview/scripts/error_handling/src/lib.cairo b/docs/listings/error_handling/src/lib.cairo similarity index 100% rename from docs/listings/sncast_overview/scripts/error_handling/src/lib.cairo rename to docs/listings/error_handling/src/lib.cairo diff --git a/docs/listings/failing_example/Scarb.toml b/docs/listings/failing_example/Scarb.toml new file mode 100644 index 0000000000..890df4147f --- /dev/null +++ b/docs/listings/failing_example/Scarb.toml @@ -0,0 +1,18 @@ +[package] +name = "failing_example" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/failing_example/src/lib.cairo b/docs/listings/failing_example/src/lib.cairo new file mode 100644 index 0000000000..4955786578 --- /dev/null +++ b/docs/listings/failing_example/src/lib.cairo @@ -0,0 +1,25 @@ +#[starknet::interface] +pub trait IHelloStarknet { + fn increase_balance(ref self: TContractState, amount: felt252); + fn get_balance(self: @TContractState) -> felt252; +} + +#[starknet::contract] +mod HelloStarknet { + #[storage] + struct Storage { + balance: felt252, + } + + #[abi(embed_v0)] + impl HelloStarknetImpl of super::IHelloStarknet { + fn increase_balance(ref self: ContractState, amount: felt252) { + assert(amount != 0, 'Amount cannot be 0'); + self.balance.write(self.balance.read() + amount); + } + + fn get_balance(self: @ContractState) -> felt252 { + self.balance.read() + } + } +} diff --git a/docs/listings/failing_example/tests/lib.cairo b/docs/listings/failing_example/tests/lib.cairo new file mode 100644 index 0000000000..42968bcd13 --- /dev/null +++ b/docs/listings/failing_example/tests/lib.cairo @@ -0,0 +1,15 @@ +#[test] +fn test_abc() { + assert(1 == 1, 1); +} + +#[test] +fn test_failing() { + assert(1 == 2, 'failing check'); +} + +#[test] +fn test_xyz() { + assert(1 == 1, 1); +} + diff --git a/docs/listings/sncast_library/scripts/declare/Scarb.toml b/docs/listings/first_test/Scarb.toml similarity index 57% rename from docs/listings/sncast_library/scripts/declare/Scarb.toml rename to docs/listings/first_test/Scarb.toml index b7fbb9f0e9..1b14dabb57 100644 --- a/docs/listings/sncast_library/scripts/declare/Scarb.toml +++ b/docs/listings/first_test/Scarb.toml @@ -1,16 +1,15 @@ [package] -name = "declare" +name = "first_test" version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -sncast_std.workspace = true +starknet = "2.7.0" -[[target.starknet-contract]] -sierra = true +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } -[[target.lib]] +[[target.starknet-contract]] sierra = true [scripts] diff --git a/docs/listings/snforge_overview/crates/writing_tests/src/first_test.cairo b/docs/listings/first_test/src/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/writing_tests/src/first_test.cairo rename to docs/listings/first_test/src/lib.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fork_testing/Scarb.toml b/docs/listings/fork_testing/Scarb.toml similarity index 72% rename from docs/listings/snforge_advanced_features/crates/fork_testing/Scarb.toml rename to docs/listings/fork_testing/Scarb.toml index ba8e0d5257..7bc4b5d822 100644 --- a/docs/listings/snforge_advanced_features/crates/fork_testing/Scarb.toml +++ b/docs/listings/fork_testing/Scarb.toml @@ -4,9 +4,11 @@ version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -snforge_std.workspace = true -assert_macros.workspace = true +starknet = "2.7.0" +assert_macros = "0.1.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } [[target.starknet-contract]] sierra = true diff --git a/docs/listings/snforge_advanced_features/crates/fork_testing/src/lib.cairo b/docs/listings/fork_testing/src/lib.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fork_testing/src/lib.cairo rename to docs/listings/fork_testing/src/lib.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fork_testing/tests/explicit.cairo b/docs/listings/fork_testing/tests/explicit.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fork_testing/tests/explicit.cairo rename to docs/listings/fork_testing/tests/explicit.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fork_testing/tests/explicit/block_hash.cairo b/docs/listings/fork_testing/tests/explicit/block_hash.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fork_testing/tests/explicit/block_hash.cairo rename to docs/listings/fork_testing/tests/explicit/block_hash.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fork_testing/tests/explicit/block_number.cairo b/docs/listings/fork_testing/tests/explicit/block_number.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fork_testing/tests/explicit/block_number.cairo rename to docs/listings/fork_testing/tests/explicit/block_number.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fork_testing/tests/explicit/block_tag.cairo b/docs/listings/fork_testing/tests/explicit/block_tag.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fork_testing/tests/explicit/block_tag.cairo rename to docs/listings/fork_testing/tests/explicit/block_tag.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fork_testing/tests/lib.cairo b/docs/listings/fork_testing/tests/lib.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fork_testing/tests/lib.cairo rename to docs/listings/fork_testing/tests/lib.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fork_testing/tests/name.cairo b/docs/listings/fork_testing/tests/name.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fork_testing/tests/name.cairo rename to docs/listings/fork_testing/tests/name.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fork_testing/tests/overridden_name.cairo b/docs/listings/fork_testing/tests/overridden_name.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fork_testing/tests/overridden_name.cairo rename to docs/listings/fork_testing/tests/overridden_name.cairo diff --git a/docs/listings/sncast_overview/scripts/full_example/Scarb.toml b/docs/listings/full_example/Scarb.toml similarity index 73% rename from docs/listings/sncast_overview/scripts/full_example/Scarb.toml rename to docs/listings/full_example/Scarb.toml index e4e6c65dee..5f8e8a5da7 100644 --- a/docs/listings/sncast_overview/scripts/full_example/Scarb.toml +++ b/docs/listings/full_example/Scarb.toml @@ -6,9 +6,9 @@ edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] -snforge_std.workspace = true -sncast_std.workspace = true -map3 = { path = "../../crates/map3" } +snforge_std = { path = "../../../snforge_std" } +sncast_std = { path = "../../../sncast_std" } +map3 = { path = "../map3" } [[target.starknet-contract]] build-external-contracts = ["map3::MapContract"] diff --git a/docs/listings/sncast_overview/scripts/full_example/src/full_example.cairo b/docs/listings/full_example/src/full_example.cairo similarity index 100% rename from docs/listings/sncast_overview/scripts/full_example/src/full_example.cairo rename to docs/listings/full_example/src/full_example.cairo diff --git a/docs/listings/sncast_overview/scripts/full_example/src/lib.cairo b/docs/listings/full_example/src/lib.cairo similarity index 100% rename from docs/listings/sncast_overview/scripts/full_example/src/lib.cairo rename to docs/listings/full_example/src/lib.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fuzz_testing/Scarb.toml b/docs/listings/fuzz_testing/Scarb.toml similarity index 57% rename from docs/listings/snforge_advanced_features/crates/fuzz_testing/Scarb.toml rename to docs/listings/fuzz_testing/Scarb.toml index 147edffe81..a0a0076d75 100644 --- a/docs/listings/snforge_advanced_features/crates/fuzz_testing/Scarb.toml +++ b/docs/listings/fuzz_testing/Scarb.toml @@ -4,8 +4,11 @@ version = "0.1.0" edition = "2023_11" [dependencies] -snforge_std.workspace = true -assert_macros.workspace = true +starknet = "2.7.0" +assert_macros = "0.1.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } [[target.lib]] sierra = true diff --git a/docs/listings/snforge_advanced_features/crates/fuzz_testing/src/basic_example.cairo b/docs/listings/fuzz_testing/src/basic_example.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fuzz_testing/src/basic_example.cairo rename to docs/listings/fuzz_testing/src/basic_example.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fuzz_testing/src/lib.cairo b/docs/listings/fuzz_testing/src/lib.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fuzz_testing/src/lib.cairo rename to docs/listings/fuzz_testing/src/lib.cairo diff --git a/docs/listings/snforge_advanced_features/crates/fuzz_testing/src/with_parameters.cairo b/docs/listings/fuzz_testing/src/with_parameters.cairo similarity index 100% rename from docs/listings/snforge_advanced_features/crates/fuzz_testing/src/with_parameters.cairo rename to docs/listings/fuzz_testing/src/with_parameters.cairo diff --git a/docs/listings/sncast_library/scripts/get_nonce/Scarb.toml b/docs/listings/get_nonce/Scarb.toml similarity index 58% rename from docs/listings/sncast_library/scripts/get_nonce/Scarb.toml rename to docs/listings/get_nonce/Scarb.toml index d908abfca9..3050533742 100644 --- a/docs/listings/sncast_library/scripts/get_nonce/Scarb.toml +++ b/docs/listings/get_nonce/Scarb.toml @@ -4,9 +4,9 @@ version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -snforge_std.workspace = true -sncast_std.workspace = true +starknet = "2.7.0" +snforge_std = { path = "../../../snforge_std" } +sncast_std = { path = "../../../sncast_std" } [[target.starknet-contract]] sierra = true diff --git a/docs/listings/sncast_library/scripts/get_nonce/src/lib.cairo b/docs/listings/get_nonce/src/lib.cairo similarity index 100% rename from docs/listings/sncast_library/scripts/get_nonce/src/lib.cairo rename to docs/listings/get_nonce/src/lib.cairo diff --git a/docs/listings/hello_snforge/Scarb.toml b/docs/listings/hello_snforge/Scarb.toml new file mode 100644 index 0000000000..364c9066d0 --- /dev/null +++ b/docs/listings/hello_snforge/Scarb.toml @@ -0,0 +1,18 @@ +[package] +name = "hello_snforge" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/hello_snforge/src/lib.cairo b/docs/listings/hello_snforge/src/lib.cairo new file mode 100644 index 0000000000..4955786578 --- /dev/null +++ b/docs/listings/hello_snforge/src/lib.cairo @@ -0,0 +1,25 @@ +#[starknet::interface] +pub trait IHelloStarknet { + fn increase_balance(ref self: TContractState, amount: felt252); + fn get_balance(self: @TContractState) -> felt252; +} + +#[starknet::contract] +mod HelloStarknet { + #[storage] + struct Storage { + balance: felt252, + } + + #[abi(embed_v0)] + impl HelloStarknetImpl of super::IHelloStarknet { + fn increase_balance(ref self: ContractState, amount: felt252) { + assert(amount != 0, 'Amount cannot be 0'); + self.balance.write(self.balance.read() + amount); + } + + fn get_balance(self: @ContractState) -> felt252 { + self.balance.read() + } + } +} diff --git a/docs/listings/hello_snforge/tests/test_contract.cairo b/docs/listings/hello_snforge/tests/test_contract.cairo new file mode 100644 index 0000000000..a747a94cd9 --- /dev/null +++ b/docs/listings/hello_snforge/tests/test_contract.cairo @@ -0,0 +1,14 @@ +#[test] +fn test_executing() { + assert(1 == 1, 1); +} + +#[test] +fn test_calling() { + assert(2 == 2, 2); +} + +#[test] +fn test_calling_another() { + assert(3 == 3, 3); +} diff --git a/docs/listings/hello_starknet/Scarb.toml b/docs/listings/hello_starknet/Scarb.toml new file mode 100644 index 0000000000..717985204b --- /dev/null +++ b/docs/listings/hello_starknet/Scarb.toml @@ -0,0 +1,18 @@ +[package] +name = "hello_starknet" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/hello_starknet/src/lib.cairo b/docs/listings/hello_starknet/src/lib.cairo new file mode 100644 index 0000000000..4955786578 --- /dev/null +++ b/docs/listings/hello_starknet/src/lib.cairo @@ -0,0 +1,25 @@ +#[starknet::interface] +pub trait IHelloStarknet { + fn increase_balance(ref self: TContractState, amount: felt252); + fn get_balance(self: @TContractState) -> felt252; +} + +#[starknet::contract] +mod HelloStarknet { + #[storage] + struct Storage { + balance: felt252, + } + + #[abi(embed_v0)] + impl HelloStarknetImpl of super::IHelloStarknet { + fn increase_balance(ref self: ContractState, amount: felt252) { + assert(amount != 0, 'Amount cannot be 0'); + self.balance.write(self.balance.read() + amount); + } + + fn get_balance(self: @ContractState) -> felt252 { + self.balance.read() + } + } +} diff --git a/docs/listings/hello_starknet/tests/test_contract.cairo b/docs/listings/hello_starknet/tests/test_contract.cairo new file mode 100644 index 0000000000..b1f395afc1 --- /dev/null +++ b/docs/listings/hello_starknet/tests/test_contract.cairo @@ -0,0 +1,47 @@ +use starknet::ContractAddress; + +use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; + +use hello_starknet::IHelloStarknetSafeDispatcher; +use hello_starknet::IHelloStarknetSafeDispatcherTrait; +use hello_starknet::IHelloStarknetDispatcher; +use hello_starknet::IHelloStarknetDispatcherTrait; + +fn deploy_contract(name: ByteArray) -> ContractAddress { + let contract = declare(name).unwrap().contract_class(); + let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); + contract_address +} + +#[test] +fn test_increase_balance() { + let contract_address = deploy_contract("HelloStarknet"); + + let dispatcher = IHelloStarknetDispatcher { contract_address }; + + let balance_before = dispatcher.get_balance(); + assert(balance_before == 0, 'Invalid balance'); + + dispatcher.increase_balance(42); + + let balance_after = dispatcher.get_balance(); + assert(balance_after == 42, 'Invalid balance'); +} + +#[test] +#[feature("safe_dispatcher")] +fn test_cannot_increase_balance_with_zero_value() { + let contract_address = deploy_contract("HelloStarknet"); + + let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address }; + + let balance_before = safe_dispatcher.get_balance().unwrap(); + assert(balance_before == 0, 'Invalid balance'); + + match safe_dispatcher.increase_balance(0) { + Result::Ok(_) => core::panic_with_felt252('Should have panicked'), + Result::Err(panic_data) => { + assert(*panic_data.at(0) == 'Amount cannot be 0', *panic_data.at(0)); + } + }; +} diff --git a/docs/listings/hello_workspaces/Scarb.toml b/docs/listings/hello_workspaces/Scarb.toml new file mode 100644 index 0000000000..20fec0edce --- /dev/null +++ b/docs/listings/hello_workspaces/Scarb.toml @@ -0,0 +1,37 @@ +[workspace] +members = [ + "crates/*", +] + +[workspace.scripts] +test = "snforge" + +[workspace.tool.snforge] + +[workspace.package] +version = "0.1.0" + +[package] +name = "hello_workspaces" +version.workspace = true +edition = "2023_10" + +[scripts] +test.workspace = true + +[tool] +snforge.workspace = true + +[dependencies] +fibonacci = { path = "crates/fibonacci" } +addition = { path = "crates/addition" } +starknet = "2.7.0" + +[dev-dependencies] +snforge_std.workspace = true + +[workspace.dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true diff --git a/docs/listings/hello_workspaces/crates/addition/Scarb.toml b/docs/listings/hello_workspaces/crates/addition/Scarb.toml new file mode 100644 index 0000000000..d67f97584f --- /dev/null +++ b/docs/listings/hello_workspaces/crates/addition/Scarb.toml @@ -0,0 +1,15 @@ +[package] +name = "addition" +version.workspace = true +edition = "2023_10" + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std.workspace = true + +[[target.starknet-contract]] +sierra = true + +[lib] diff --git a/docs/listings/hello_workspaces/crates/addition/src/lib.cairo b/docs/listings/hello_workspaces/crates/addition/src/lib.cairo new file mode 100644 index 0000000000..f53d0a379c --- /dev/null +++ b/docs/listings/hello_workspaces/crates/addition/src/lib.cairo @@ -0,0 +1,33 @@ +fn add(a: felt252, b: felt252) -> felt252 { + a + b +} + +#[starknet::interface] +trait IAdditionContract { + fn answer(ref self: TContractState) -> felt252; +} + +#[starknet::contract] +mod AdditionContract { + use addition::add; + + #[storage] + struct Storage {} + + #[abi(embed_v0)] + impl AdditionContractImpl of super::IAdditionContract { + fn answer(ref self: ContractState) -> felt252 { + add(10, 20) + } + } +} + +#[cfg(test)] +mod tests { + use super::add; + + #[test] + fn it_works() { + assert(add(2, 3) == 5, 'it works!'); + } +} diff --git a/docs/listings/hello_workspaces/crates/addition/tests/nested.cairo b/docs/listings/hello_workspaces/crates/addition/tests/nested.cairo new file mode 100644 index 0000000000..1ee9db3963 --- /dev/null +++ b/docs/listings/hello_workspaces/crates/addition/tests/nested.cairo @@ -0,0 +1,17 @@ +use snforge_std::declare; + +mod test_nested; + +fn foo() -> u8 { + 2 +} + +#[test] +fn simple_case() { + assert(1 == 1, 'simple check'); +} + +#[test] +fn contract_test() { + declare("AdditionContract").unwrap(); +} diff --git a/docs/listings/hello_workspaces/crates/addition/tests/nested/test_nested.cairo b/docs/listings/hello_workspaces/crates/addition/tests/nested/test_nested.cairo new file mode 100644 index 0000000000..5aa19b8adf --- /dev/null +++ b/docs/listings/hello_workspaces/crates/addition/tests/nested/test_nested.cairo @@ -0,0 +1,12 @@ +use super::foo; + +#[test] +fn test_two() { + assert(foo() == 2, 'foo() == 2'); +} + +#[test] +fn test_two_and_two() { + assert(2 == 2, '2 == 2'); + assert(2 == 2, '2 == 2'); +} diff --git a/docs/listings/hello_workspaces/crates/fibonacci/Scarb.toml b/docs/listings/hello_workspaces/crates/fibonacci/Scarb.toml new file mode 100644 index 0000000000..45378edf34 --- /dev/null +++ b/docs/listings/hello_workspaces/crates/fibonacci/Scarb.toml @@ -0,0 +1,23 @@ +[package] +name = "fibonacci" +version.workspace = true +edition = "2023_10" + +[scripts] +test.workspace = true + +[tool] +snforge.workspace = true + +[dependencies] +addition = { path = "../addition" } +starknet = "2.7.0" + +[dev-dependencies] +snforge_std.workspace = true + +[[target.starknet-contract]] +sierra = true +build-external-contracts = ["addition::AdditionContract"] + +[lib] diff --git a/docs/listings/hello_workspaces/crates/fibonacci/src/lib.cairo b/docs/listings/hello_workspaces/crates/fibonacci/src/lib.cairo new file mode 100644 index 0000000000..005d6453f2 --- /dev/null +++ b/docs/listings/hello_workspaces/crates/fibonacci/src/lib.cairo @@ -0,0 +1,39 @@ +use addition::add; + +fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { + match n { + 0 => a, + _ => fib(b, add(a, b), n - 1), + } +} + +#[starknet::contract] +mod FibonacciContract { + use addition::add; + use fibonacci::fib; + + #[storage] + struct Storage {} + + #[abi(embed_v0)] + fn answer(ref self: ContractState) -> felt252 { + add(fib(0, 1, 16), fib(0, 1, 8)) + } +} + +#[cfg(test)] +mod tests { + use super::fib; + use snforge_std::declare; + + #[test] + fn it_works() { + assert(fib(0, 1, 16) == 987, 'it works!'); + } + + #[test] + fn contract_test() { + declare("FibonacciContract").unwrap(); + declare("AdditionContract").unwrap(); + } +} diff --git a/docs/listings/hello_workspaces/crates/fibonacci/tests/abc.cairo b/docs/listings/hello_workspaces/crates/fibonacci/tests/abc.cairo new file mode 100644 index 0000000000..8fbad19666 --- /dev/null +++ b/docs/listings/hello_workspaces/crates/fibonacci/tests/abc.cairo @@ -0,0 +1,10 @@ +mod efg; + +#[test] +fn abc_test() { + assert(foo() == 1, ''); +} + +fn foo() -> u8 { + 1 +} diff --git a/docs/listings/hello_workspaces/crates/fibonacci/tests/abc/efg.cairo b/docs/listings/hello_workspaces/crates/fibonacci/tests/abc/efg.cairo new file mode 100644 index 0000000000..b0c8f2a8b8 --- /dev/null +++ b/docs/listings/hello_workspaces/crates/fibonacci/tests/abc/efg.cairo @@ -0,0 +1,9 @@ +#[test] +fn efg_test() { + assert(super::foo() == 1, ''); +} + +#[test] +fn failing_test() { + assert(1 == 2, ''); +} diff --git a/docs/listings/hello_workspaces/crates/fibonacci/tests/lib.cairo b/docs/listings/hello_workspaces/crates/fibonacci/tests/lib.cairo new file mode 100644 index 0000000000..582c1efab6 --- /dev/null +++ b/docs/listings/hello_workspaces/crates/fibonacci/tests/lib.cairo @@ -0,0 +1,6 @@ +mod abc; + +#[test] +fn lib_test() { + assert(abc::foo() == 1, ''); +} diff --git a/docs/listings/hello_workspaces/crates/fibonacci/tests/not_collected.cairo b/docs/listings/hello_workspaces/crates/fibonacci/tests/not_collected.cairo new file mode 100644 index 0000000000..a4c1b8d76b --- /dev/null +++ b/docs/listings/hello_workspaces/crates/fibonacci/tests/not_collected.cairo @@ -0,0 +1,6 @@ +// should not be collected + +#[test] +fn not_collected() { + assert(1 == 1, ''); +} diff --git a/docs/listings/hello_workspaces/src/lib.cairo b/docs/listings/hello_workspaces/src/lib.cairo new file mode 100644 index 0000000000..a93dc5d461 --- /dev/null +++ b/docs/listings/hello_workspaces/src/lib.cairo @@ -0,0 +1,28 @@ +#[starknet::interface] +trait IFibContract { + fn answer(ref self: TContractState) -> felt252; +} + +#[starknet::contract] +mod FibContract { + use addition::add; + use fibonacci::fib; + + #[storage] + struct Storage {} + + #[abi(embed_v0)] + impl FibContractImpl of super::IFibContract { + fn answer(ref self: ContractState) -> felt252 { + add(fib(0, 1, 16), fib(0, 1, 8)) + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_simple() { + assert(1 == 1, 1); + } +} diff --git a/docs/listings/hello_workspaces/tests/test_failing.cairo b/docs/listings/hello_workspaces/tests/test_failing.cairo new file mode 100644 index 0000000000..864786023b --- /dev/null +++ b/docs/listings/hello_workspaces/tests/test_failing.cairo @@ -0,0 +1,9 @@ +#[test] +fn test_failing() { + assert(1 == 2, 'failing check'); +} + +#[test] +fn test_another_failing() { + assert(2 == 3, 'failing check'); +} diff --git a/docs/listings/ignoring_example/Scarb.toml b/docs/listings/ignoring_example/Scarb.toml new file mode 100644 index 0000000000..6ce2d88b2d --- /dev/null +++ b/docs/listings/ignoring_example/Scarb.toml @@ -0,0 +1,16 @@ +[package] +name = "ignoring_example" +version = "0.1.0" +edition = "2023_11" + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/ignoring_example/src/lib.cairo b/docs/listings/ignoring_example/src/lib.cairo new file mode 100644 index 0000000000..f76a6eede4 --- /dev/null +++ b/docs/listings/ignoring_example/src/lib.cairo @@ -0,0 +1,8 @@ +#[cfg(test)] +mod tests { + #[test] + #[ignore] + fn ignored_test() { // test code + } +} + diff --git a/docs/listings/sncast_library/Scarb.toml b/docs/listings/invoke/Scarb.toml similarity index 70% rename from docs/listings/sncast_library/Scarb.toml rename to docs/listings/invoke/Scarb.toml index 660b958f63..55ed304196 100644 --- a/docs/listings/sncast_library/Scarb.toml +++ b/docs/listings/invoke/Scarb.toml @@ -1,7 +1,9 @@ -[workspace] -members = ["scripts/*"] +[package] +name = "invoke" +version = "0.1.0" +edition = "2023_11" -[workspace.dependencies] +[dependencies] starknet = "2.7.0" snforge_std = { path = "../../../snforge_std" } sncast_std = { path = "../../../sncast_std" } diff --git a/docs/listings/sncast_library/scripts/invoke/src/lib.cairo b/docs/listings/invoke/src/lib.cairo similarity index 100% rename from docs/listings/sncast_library/scripts/invoke/src/lib.cairo rename to docs/listings/invoke/src/lib.cairo diff --git a/docs/listings/sncast_overview/crates/map3/Scarb.toml b/docs/listings/map3/Scarb.toml similarity index 73% rename from docs/listings/sncast_overview/crates/map3/Scarb.toml rename to docs/listings/map3/Scarb.toml index 4dec1caab6..0e4e7d558a 100644 --- a/docs/listings/sncast_overview/crates/map3/Scarb.toml +++ b/docs/listings/map3/Scarb.toml @@ -10,8 +10,8 @@ sierra = true sierra = false [dependencies] -snforge_std.workspace = true -starknet.workspace = true +snforge_std = { path = "../../../snforge_std" } +starknet = "2.7.0" [scripts] test = "snforge test" diff --git a/docs/listings/sncast_overview/snfoundry.toml b/docs/listings/map3/snfoundry.toml similarity index 100% rename from docs/listings/sncast_overview/snfoundry.toml rename to docs/listings/map3/snfoundry.toml diff --git a/docs/listings/sncast_overview/crates/map3/src/lib.cairo b/docs/listings/map3/src/lib.cairo similarity index 100% rename from docs/listings/sncast_overview/crates/map3/src/lib.cairo rename to docs/listings/map3/src/lib.cairo diff --git a/docs/listings/panicking_test/Scarb.toml b/docs/listings/panicking_test/Scarb.toml new file mode 100644 index 0000000000..ef03e9c5e4 --- /dev/null +++ b/docs/listings/panicking_test/Scarb.toml @@ -0,0 +1,16 @@ +[package] +name = "panicking_test" +version = "0.1.0" +edition = "2023_11" + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/snforge_overview/crates/writing_tests/src/panicking_tests.cairo b/docs/listings/panicking_test/src/lib.cairo similarity index 54% rename from docs/listings/snforge_overview/crates/writing_tests/src/panicking_tests.cairo rename to docs/listings/panicking_test/src/lib.cairo index f9701e34dd..eea1bbbdfe 100644 --- a/docs/listings/snforge_overview/crates/writing_tests/src/panicking_tests.cairo +++ b/docs/listings/panicking_test/src/lib.cairo @@ -1,7 +1,6 @@ -//ANCHOR:first_half fn panicking_function() { let mut data = array![]; - data.append('aaa'); + data.append('panic message'); panic(data) } @@ -10,14 +9,8 @@ mod tests { use super::panicking_function; #[test] - //ANCHOR_END:first_half - #[should_panic(expected: 'aaa')] - //ANCHOR:second_half fn failing() { panicking_function(); assert(2 == 2, '2 == 2'); } } -//ANCHOR_END:second_half - -mod dummy {} // trick `scarb fmt --check` diff --git a/docs/listings/should_panic_example/Scarb.toml b/docs/listings/should_panic_example/Scarb.toml new file mode 100644 index 0000000000..7980eeca8d --- /dev/null +++ b/docs/listings/should_panic_example/Scarb.toml @@ -0,0 +1,16 @@ +[package] +name = "should_panic_example" +version = "0.1.0" +edition = "2023_11" + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/should_panic_example/src/lib.cairo b/docs/listings/should_panic_example/src/lib.cairo new file mode 100644 index 0000000000..fc59c2ac77 --- /dev/null +++ b/docs/listings/should_panic_example/src/lib.cairo @@ -0,0 +1,45 @@ +#[cfg(test)] +mod tests { + //ANCHOR:byte_array + #[test] + #[should_panic(expected: "This will panic")] + fn should_panic_exact() { + panic!("This will panic"); + } + + // here the expected message is a substring of the actual message + #[test] + #[should_panic(expected: "will panic")] + fn should_panic_expected_is_substring() { + panic!("This will panic"); + } + //ANCHOR_END:byte_array + + //ANCHOR:felt + #[test] + #[should_panic(expected: 'panic message')] + fn should_panic_felt_matching() { + assert(1 != 1, 'panic message'); + } + //ANCHOR_END:felt + + //ANCHOR:tuple + use core::panic_with_felt252; + + #[test] + #[should_panic(expected: ('panic message',))] + fn should_panic_check_data() { + panic_with_felt252('panic message'); + } + + // works for multiple messages + #[test] + #[should_panic(expected: ('panic message', 'second message',))] + fn should_panic_multiple_messages() { + let mut arr = ArrayTrait::new(); + arr.append('panic message'); + arr.append('second message'); + panic(arr); + } + //ANCHOR_END:tuple +} diff --git a/docs/listings/sncast_library/scripts/invoke/Scarb.toml b/docs/listings/sncast_library/scripts/invoke/Scarb.toml deleted file mode 100644 index 1936363e8c..0000000000 --- a/docs/listings/sncast_library/scripts/invoke/Scarb.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "invoke" -version = "0.1.0" -edition = "2023_11" - -[dependencies] -starknet.workspace = true -snforge_std.workspace = true -sncast_std.workspace = true - -[[target.starknet-contract]] -sierra = true - -[scripts] -test = "snforge test" diff --git a/docs/listings/sncast_overview/.gitignore b/docs/listings/sncast_overview/.gitignore deleted file mode 100644 index a1d8daa50d..0000000000 --- a/docs/listings/sncast_overview/.gitignore +++ /dev/null @@ -1 +0,0 @@ -scripts/**/*state.json diff --git a/docs/listings/snforge_overview/crates/testing_smart_contracts/Scarb.toml b/docs/listings/snforge_overview/crates/testing_smart_contracts/Scarb.toml deleted file mode 100644 index 2a22bc631a..0000000000 --- a/docs/listings/snforge_overview/crates/testing_smart_contracts/Scarb.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "testing_smart_contracts" -version = "0.1.0" -edition = "2023_11" - -[dependencies] -starknet.workspace = true -snforge_std.workspace = true - -[[target.starknet-contract]] -sierra = true - -[scripts] -test = "snforge test" diff --git a/docs/listings/snforge_overview/crates/testing_smart_contracts/src/lib.cairo b/docs/listings/snforge_overview/crates/testing_smart_contracts/src/lib.cairo deleted file mode 100644 index 7a2182058d..0000000000 --- a/docs/listings/snforge_overview/crates/testing_smart_contracts/src/lib.cairo +++ /dev/null @@ -1,2 +0,0 @@ -pub mod simple_contract; -pub mod handling_errors; diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/Scarb.toml b/docs/listings/snforge_overview/crates/using_cheatcodes/Scarb.toml deleted file mode 100644 index 537128dcfb..0000000000 --- a/docs/listings/snforge_overview/crates/using_cheatcodes/Scarb.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "using_cheatcodes" -version = "0.1.0" -edition = "2023_11" - -[dependencies] -starknet.workspace = true -snforge_std.workspace = true -assert_macros.workspace = true - -[[target.starknet-contract]] -sierra = true - -[scripts] -test = "snforge test" diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address.cairo b/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address.cairo deleted file mode 100644 index 5d6f615f9c..0000000000 --- a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address.cairo +++ /dev/null @@ -1,5 +0,0 @@ -pub mod failing; -pub mod proper_use; -pub mod proper_use_global; -pub mod cancel; -pub mod span; diff --git a/docs/listings/snforge_overview/crates/writing_tests/Scarb.toml b/docs/listings/snforge_overview/crates/writing_tests/Scarb.toml deleted file mode 100644 index f3f793dab6..0000000000 --- a/docs/listings/snforge_overview/crates/writing_tests/Scarb.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "writing_tests" -version = "0.1.0" -edition = "2023_11" - -[dependencies] -starknet.workspace = true -snforge_std.workspace = true - -[[target.starknet-contract]] -sierra = true - -[scripts] -test = "snforge test" diff --git a/docs/listings/snforge_overview/crates/writing_tests/src/lib.cairo b/docs/listings/snforge_overview/crates/writing_tests/src/lib.cairo deleted file mode 100644 index dd518b4752..0000000000 --- a/docs/listings/snforge_overview/crates/writing_tests/src/lib.cairo +++ /dev/null @@ -1,2 +0,0 @@ -pub mod first_test; -pub mod panicking_tests; diff --git a/docs/listings/snforge_overview/crates/writing_tests/tests/expected_failures.cairo b/docs/listings/snforge_overview/crates/writing_tests/tests/expected_failures.cairo deleted file mode 100644 index da698c1a57..0000000000 --- a/docs/listings/snforge_overview/crates/writing_tests/tests/expected_failures.cairo +++ /dev/null @@ -1,44 +0,0 @@ -//ANCHOR:byte_array -#[test] -#[should_panic(expected: "This will panic")] -fn should_panic_exact() { - panic!("This will panic"); -} - -// here the expected message is a substring of the actual message -#[test] -#[should_panic(expected: "will panic")] -fn should_panic_expected_is_substring() { - panic!("This will panic"); -} -//ANCHOR_END:byte_array - -//ANCHOR:felt -#[test] -#[should_panic(expected: 'panic message')] -fn should_panic_felt_matching() { - assert(1 != 1, 'panic message'); -} -//ANCHOR_END:felt - -//ANCHOR:tuple -use core::panic_with_felt252; - -#[test] -#[should_panic(expected: ('panic message',))] -fn should_panic_check_data() { - panic_with_felt252('panic message'); -} - -// works for multiple messages -#[test] -#[should_panic(expected: ('panic message', 'second message',))] -fn should_panic_multiple_messages() { - let mut arr = ArrayTrait::new(); - arr.append('panic message'); - arr.append('second message'); - panic(arr); -} -//ANCHOR_END:tuple - -mod dummy {} // trick `scarb fmt -c` diff --git a/docs/listings/snforge_overview/crates/writing_tests/tests/ignoring.cairo b/docs/listings/snforge_overview/crates/writing_tests/tests/ignoring.cairo deleted file mode 100644 index e9d1988135..0000000000 --- a/docs/listings/snforge_overview/crates/writing_tests/tests/ignoring.cairo +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -#[ignore] -fn ignored_test() { // test code -} diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/Scarb.toml b/docs/listings/testing_contract_internals/Scarb.toml similarity index 67% rename from docs/listings/snforge_overview/crates/testing_contract_internals/Scarb.toml rename to docs/listings/testing_contract_internals/Scarb.toml index fd0ff87b5c..05aab81c8a 100644 --- a/docs/listings/snforge_overview/crates/testing_contract_internals/Scarb.toml +++ b/docs/listings/testing_contract_internals/Scarb.toml @@ -4,8 +4,10 @@ version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -snforge_std.workspace = true +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } [[target.starknet-contract]] sierra = true diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/src/basic_example.cairo b/docs/listings/testing_contract_internals/src/basic_example.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/src/basic_example.cairo rename to docs/listings/testing_contract_internals/src/basic_example.cairo diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/src/lib.cairo b/docs/listings/testing_contract_internals/src/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/src/lib.cairo rename to docs/listings/testing_contract_internals/src/lib.cairo diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/src/spying_for_events.cairo b/docs/listings/testing_contract_internals/src/spying_for_events.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/src/spying_for_events.cairo rename to docs/listings/testing_contract_internals/src/spying_for_events.cairo diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/src/using_library_calls.cairo b/docs/listings/testing_contract_internals/src/using_library_calls.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/src/using_library_calls.cairo rename to docs/listings/testing_contract_internals/src/using_library_calls.cairo diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/tests/lib.cairo b/docs/listings/testing_contract_internals/tests/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/tests/lib.cairo rename to docs/listings/testing_contract_internals/tests/lib.cairo diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/tests/mocking_the_context_info.cairo b/docs/listings/testing_contract_internals/tests/mocking_the_context_info.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/tests/mocking_the_context_info.cairo rename to docs/listings/testing_contract_internals/tests/mocking_the_context_info.cairo diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/tests/spying_for_events.cairo b/docs/listings/testing_contract_internals/tests/spying_for_events.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/tests/spying_for_events.cairo rename to docs/listings/testing_contract_internals/tests/spying_for_events.cairo diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/tests/spying_for_events/syscall_tests.cairo b/docs/listings/testing_contract_internals/tests/spying_for_events/syscall_tests.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/tests/spying_for_events/syscall_tests.cairo rename to docs/listings/testing_contract_internals/tests/spying_for_events/syscall_tests.cairo diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/tests/spying_for_events/tests.cairo b/docs/listings/testing_contract_internals/tests/spying_for_events/tests.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/tests/spying_for_events/tests.cairo rename to docs/listings/testing_contract_internals/tests/spying_for_events/tests.cairo diff --git a/docs/listings/snforge_overview/crates/testing_contract_internals/tests/using_library_calls.cairo b/docs/listings/testing_contract_internals/tests/using_library_calls.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_contract_internals/tests/using_library_calls.cairo rename to docs/listings/testing_contract_internals/tests/using_library_calls.cairo diff --git a/docs/listings/snforge_overview/crates/testing_events/Scarb.toml b/docs/listings/testing_events/Scarb.toml similarity index 65% rename from docs/listings/snforge_overview/crates/testing_events/Scarb.toml rename to docs/listings/testing_events/Scarb.toml index b712ac493f..bb14cc408c 100644 --- a/docs/listings/snforge_overview/crates/testing_events/Scarb.toml +++ b/docs/listings/testing_events/Scarb.toml @@ -4,8 +4,10 @@ version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -snforge_std.workspace = true +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } [[target.starknet-contract]] sierra = true diff --git a/docs/listings/snforge_overview/crates/testing_events/src/contract.cairo b/docs/listings/testing_events/src/contract.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_events/src/contract.cairo rename to docs/listings/testing_events/src/contract.cairo diff --git a/docs/listings/snforge_overview/crates/testing_events/src/lib.cairo b/docs/listings/testing_events/src/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_events/src/lib.cairo rename to docs/listings/testing_events/src/lib.cairo diff --git a/docs/listings/snforge_overview/crates/testing_events/src/syscall.cairo b/docs/listings/testing_events/src/syscall.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_events/src/syscall.cairo rename to docs/listings/testing_events/src/syscall.cairo diff --git a/docs/listings/snforge_overview/crates/testing_events/src/syscall_dummy.cairo b/docs/listings/testing_events/src/syscall_dummy.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_events/src/syscall_dummy.cairo rename to docs/listings/testing_events/src/syscall_dummy.cairo diff --git a/docs/listings/snforge_overview/crates/testing_events/tests/assert_emitted.cairo b/docs/listings/testing_events/tests/assert_emitted.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_events/tests/assert_emitted.cairo rename to docs/listings/testing_events/tests/assert_emitted.cairo diff --git a/docs/listings/snforge_overview/crates/testing_events/tests/assert_manually.cairo b/docs/listings/testing_events/tests/assert_manually.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_events/tests/assert_manually.cairo rename to docs/listings/testing_events/tests/assert_manually.cairo diff --git a/docs/listings/snforge_overview/crates/testing_events/tests/filter.cairo b/docs/listings/testing_events/tests/filter.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_events/tests/filter.cairo rename to docs/listings/testing_events/tests/filter.cairo diff --git a/docs/listings/snforge_overview/crates/testing_events/tests/syscall.cairo b/docs/listings/testing_events/tests/syscall.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_events/tests/syscall.cairo rename to docs/listings/testing_events/tests/syscall.cairo diff --git a/docs/listings/snforge_overview/crates/testing_messages_to_l1/Scarb.toml b/docs/listings/testing_messages_to_l1/Scarb.toml similarity index 66% rename from docs/listings/snforge_overview/crates/testing_messages_to_l1/Scarb.toml rename to docs/listings/testing_messages_to_l1/Scarb.toml index bee7f589eb..892751fe9f 100644 --- a/docs/listings/snforge_overview/crates/testing_messages_to_l1/Scarb.toml +++ b/docs/listings/testing_messages_to_l1/Scarb.toml @@ -4,8 +4,10 @@ version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -snforge_std.workspace = true +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } [[target.starknet-contract]] sierra = true diff --git a/docs/listings/snforge_overview/crates/testing_messages_to_l1/src/lib.cairo b/docs/listings/testing_messages_to_l1/src/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_messages_to_l1/src/lib.cairo rename to docs/listings/testing_messages_to_l1/src/lib.cairo diff --git a/docs/listings/snforge_overview/crates/testing_messages_to_l1/tests/detailed.cairo b/docs/listings/testing_messages_to_l1/tests/detailed.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_messages_to_l1/tests/detailed.cairo rename to docs/listings/testing_messages_to_l1/tests/detailed.cairo diff --git a/docs/listings/snforge_overview/crates/testing_messages_to_l1/tests/lib.cairo b/docs/listings/testing_messages_to_l1/tests/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_messages_to_l1/tests/lib.cairo rename to docs/listings/testing_messages_to_l1/tests/lib.cairo diff --git a/docs/listings/snforge_overview/crates/testing_messages_to_l1/tests/simple.cairo b/docs/listings/testing_messages_to_l1/tests/simple.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_messages_to_l1/tests/simple.cairo rename to docs/listings/testing_messages_to_l1/tests/simple.cairo diff --git a/docs/listings/testing_smart_contracts_handling_errors/Scarb.toml b/docs/listings/testing_smart_contracts_handling_errors/Scarb.toml new file mode 100644 index 0000000000..90c0ebf9ad --- /dev/null +++ b/docs/listings/testing_smart_contracts_handling_errors/Scarb.toml @@ -0,0 +1,16 @@ +[package] +name = "testing_smart_contracts_handling_errors" +version = "0.1.0" +edition = "2023_11" + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/snforge_overview/crates/testing_smart_contracts/src/handling_errors.cairo b/docs/listings/testing_smart_contracts_handling_errors/src/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_smart_contracts/src/handling_errors.cairo rename to docs/listings/testing_smart_contracts_handling_errors/src/lib.cairo diff --git a/docs/listings/snforge_overview/crates/testing_smart_contracts/tests/handle_panic.cairo b/docs/listings/testing_smart_contracts_handling_errors/tests/handle_panic.cairo similarity index 95% rename from docs/listings/snforge_overview/crates/testing_smart_contracts/tests/handle_panic.cairo rename to docs/listings/testing_smart_contracts_handling_errors/tests/handle_panic.cairo index ec8d8b2537..ea182b9100 100644 --- a/docs/listings/snforge_overview/crates/testing_smart_contracts/tests/handle_panic.cairo +++ b/docs/listings/testing_smart_contracts_handling_errors/tests/handle_panic.cairo @@ -2,7 +2,7 @@ use snforge_std::byte_array::try_deserialize_bytearray_error; use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; -use testing_smart_contracts::handling_errors::{ +use testing_smart_contracts_handling_errors::{ IPanicContractSafeDispatcher, IPanicContractSafeDispatcherTrait }; diff --git a/docs/listings/snforge_overview/crates/testing_smart_contracts/tests/panic.cairo b/docs/listings/testing_smart_contracts_handling_errors/tests/panic.cairo similarity index 84% rename from docs/listings/snforge_overview/crates/testing_smart_contracts/tests/panic.cairo rename to docs/listings/testing_smart_contracts_handling_errors/tests/panic.cairo index d42ec2108d..5bdd7006fd 100644 --- a/docs/listings/snforge_overview/crates/testing_smart_contracts/tests/panic.cairo +++ b/docs/listings/testing_smart_contracts_handling_errors/tests/panic.cairo @@ -1,13 +1,12 @@ //ANCHOR:first_half use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; -use testing_smart_contracts::handling_errors::{ +use testing_smart_contracts_handling_errors::{ IPanicContractDispatcher, IPanicContractDispatcherTrait }; #[test] //ANCHOR_END:first_half -#[should_panic(expected: ('PANIC', 'DAYTAH'))] //ANCHOR:second_half fn failing() { let contract = declare("PanicContract").unwrap().contract_class(); diff --git a/docs/listings/testing_smart_contracts_safe_dispatcher/Scarb.toml b/docs/listings/testing_smart_contracts_safe_dispatcher/Scarb.toml new file mode 100644 index 0000000000..571443a88d --- /dev/null +++ b/docs/listings/testing_smart_contracts_safe_dispatcher/Scarb.toml @@ -0,0 +1,16 @@ +[package] +name = "testing_smart_contracts_safe_dispatcher" +version = "0.1.0" +edition = "2023_11" + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/testing_smart_contracts_safe_dispatcher/src/lib.cairo b/docs/listings/testing_smart_contracts_safe_dispatcher/src/lib.cairo new file mode 100644 index 0000000000..be964fec2a --- /dev/null +++ b/docs/listings/testing_smart_contracts_safe_dispatcher/src/lib.cairo @@ -0,0 +1,26 @@ +#[starknet::interface] +pub trait IPanicContract { + fn do_a_panic(self: @TContractState); + fn do_a_string_panic(self: @TContractState); +} + +#[starknet::contract] +pub mod PanicContract { + use core::array::ArrayTrait; + + #[storage] + struct Storage {} + + #[abi(embed_v0)] + pub impl PanicContractImpl of super::IPanicContract { + // Panics + fn do_a_panic(self: @ContractState) { + panic(array!['PANIC', 'DAYTAH']); + } + + fn do_a_string_panic(self: @ContractState) { + // A macro which allows panicking with a ByteArray (string) instance + panic!("This is panicking with a string, which can be longer than 31 characters"); + } + } +} diff --git a/docs/listings/snforge_overview/crates/testing_smart_contracts/tests/safe_dispatcher.cairo b/docs/listings/testing_smart_contracts_safe_dispatcher/tests/safe_dispatcher.cairo similarity index 93% rename from docs/listings/snforge_overview/crates/testing_smart_contracts/tests/safe_dispatcher.cairo rename to docs/listings/testing_smart_contracts_safe_dispatcher/tests/safe_dispatcher.cairo index 779c9c6ab2..56ca89580a 100644 --- a/docs/listings/snforge_overview/crates/testing_smart_contracts/tests/safe_dispatcher.cairo +++ b/docs/listings/testing_smart_contracts_safe_dispatcher/tests/safe_dispatcher.cairo @@ -1,6 +1,6 @@ use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; -use testing_smart_contracts::handling_errors::{ +use testing_smart_contracts_safe_dispatcher::{ IPanicContractSafeDispatcher, IPanicContractSafeDispatcherTrait }; diff --git a/docs/listings/testing_smart_contracts_writing_tests/Scarb.toml b/docs/listings/testing_smart_contracts_writing_tests/Scarb.toml new file mode 100644 index 0000000000..234185fba5 --- /dev/null +++ b/docs/listings/testing_smart_contracts_writing_tests/Scarb.toml @@ -0,0 +1,16 @@ +[package] +name = "testing_smart_contracts_writing_tests" +version = "0.1.0" +edition = "2023_11" + +[dependencies] +starknet = "2.7.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/snforge_overview/crates/testing_smart_contracts/src/simple_contract.cairo b/docs/listings/testing_smart_contracts_writing_tests/src/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/testing_smart_contracts/src/simple_contract.cairo rename to docs/listings/testing_smart_contracts_writing_tests/src/lib.cairo diff --git a/docs/listings/snforge_overview/crates/testing_smart_contracts/tests/simple_contract.cairo b/docs/listings/testing_smart_contracts_writing_tests/tests/simple_contract.cairo similarity index 95% rename from docs/listings/snforge_overview/crates/testing_smart_contracts/tests/simple_contract.cairo rename to docs/listings/testing_smart_contracts_writing_tests/tests/simple_contract.cairo index 11a6a32451..c47b4ed808 100644 --- a/docs/listings/snforge_overview/crates/testing_smart_contracts/tests/simple_contract.cairo +++ b/docs/listings/testing_smart_contracts_writing_tests/tests/simple_contract.cairo @@ -1,6 +1,6 @@ use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; -use testing_smart_contracts::simple_contract::{ +use testing_smart_contracts_writing_tests::{ ISimpleContractDispatcher, ISimpleContractDispatcherTrait }; diff --git a/docs/listings/sncast_library/scripts/tx_status/Scarb.toml b/docs/listings/tx_status/Scarb.toml similarity index 58% rename from docs/listings/sncast_library/scripts/tx_status/Scarb.toml rename to docs/listings/tx_status/Scarb.toml index 5a3a2a08ae..1b98344e94 100644 --- a/docs/listings/sncast_library/scripts/tx_status/Scarb.toml +++ b/docs/listings/tx_status/Scarb.toml @@ -4,9 +4,9 @@ version = "0.1.0" edition = "2023_11" [dependencies] -starknet.workspace = true -snforge_std.workspace = true -sncast_std.workspace = true +starknet = "2.7.0" +snforge_std = { path = "../../../snforge_std" } +sncast_std = { path = "../../../sncast_std" } [[target.starknet-contract]] sierra = true diff --git a/docs/listings/sncast_library/scripts/tx_status/src/lib.cairo b/docs/listings/tx_status/src/lib.cairo similarity index 100% rename from docs/listings/sncast_library/scripts/tx_status/src/lib.cairo rename to docs/listings/tx_status/src/lib.cairo diff --git a/docs/listings/snforge_overview/Scarb.toml b/docs/listings/using_cheatcodes/Scarb.toml similarity index 60% rename from docs/listings/snforge_overview/Scarb.toml rename to docs/listings/using_cheatcodes/Scarb.toml index 437d4c716e..70ab8ea626 100644 --- a/docs/listings/snforge_overview/Scarb.toml +++ b/docs/listings/using_cheatcodes/Scarb.toml @@ -1,11 +1,15 @@ -[workspace] -members = ["crates/*"] +[package] +name = "using_cheatcodes" +version = "0.1.0" +edition = "2023_11" -[workspace.dependencies] +[dependencies] starknet = "2.7.0" -snforge_std = { path = "../../../snforge_std" } assert_macros = "0.1.0" +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + [[target.starknet-contract]] sierra = true diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/src/lib.cairo b/docs/listings/using_cheatcodes/src/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/using_cheatcodes/src/lib.cairo rename to docs/listings/using_cheatcodes/src/lib.cairo diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/failing.cairo b/docs/listings/using_cheatcodes/tests/lib.cairo similarity index 77% rename from docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/failing.cairo rename to docs/listings/using_cheatcodes/tests/lib.cairo index a44eac2e7e..646dfdf4ea 100644 --- a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/failing.cairo +++ b/docs/listings/using_cheatcodes/tests/lib.cairo @@ -1,11 +1,7 @@ -//ANCHOR:first_half use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; use using_cheatcodes::{ICheatcodeCheckerDispatcher, ICheatcodeCheckerDispatcherTrait}; #[test] -//ANCHOR_END:first_half -#[should_panic(expected: 'user is not allowed')] -//ANCHOR:second_half fn call_and_invoke() { let contract = declare("CheatcodeChecker").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@array![]).unwrap(); @@ -19,6 +15,3 @@ fn call_and_invoke() { let balance = dispatcher.get_balance(); assert(balance == 100, 'balance == 100'); } -//ANCHOR_END:second_half - -mod dummy {} // trick `scarb fmt -c` diff --git a/docs/listings/using_cheatcodes_cancelling_cheat/Scarb.toml b/docs/listings/using_cheatcodes_cancelling_cheat/Scarb.toml new file mode 100644 index 0000000000..ec3efea5ea --- /dev/null +++ b/docs/listings/using_cheatcodes_cancelling_cheat/Scarb.toml @@ -0,0 +1,17 @@ +[package] +name = "using_cheatcodes_cancelling_cheat" +version = "0.1.0" +edition = "2023_11" + +[dependencies] +starknet = "2.7.0" +assert_macros = "0.1.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/using_cheatcodes_cancelling_cheat/src/lib.cairo b/docs/listings/using_cheatcodes_cancelling_cheat/src/lib.cairo new file mode 100644 index 0000000000..b6c20ffcad --- /dev/null +++ b/docs/listings/using_cheatcodes_cancelling_cheat/src/lib.cairo @@ -0,0 +1,55 @@ +#[starknet::interface] +pub trait ICheatcodeChecker { + fn increase_balance(ref self: TContractState, amount: felt252); + fn get_balance(self: @TContractState) -> felt252; + fn get_block_number_at_construction(self: @TContractState) -> u64; + fn get_block_timestamp_at_construction(self: @TContractState) -> u64; +} + +#[starknet::contract] +pub mod CheatcodeChecker { + use core::box::BoxTrait; + use starknet::get_caller_address; + + #[storage] + struct Storage { + balance: felt252, + blk_nb: u64, + blk_timestamp: u64, + } + + #[constructor] + fn constructor(ref self: ContractState) { + // store the current block number + self.blk_nb.write(starknet::get_block_info().unbox().block_number); + // store the current block timestamp + self.blk_timestamp.write(starknet::get_block_info().unbox().block_timestamp); + } + + #[abi(embed_v0)] + impl ICheatcodeCheckerImpl of super::ICheatcodeChecker { + // Increases the balance by the given amount + fn increase_balance(ref self: ContractState, amount: felt252) { + assert_is_allowed_user(); + self.balance.write(self.balance.read() + amount); + } + // Gets the balance. + fn get_balance(self: @ContractState) -> felt252 { + self.balance.read() + } + // Gets the block number + fn get_block_number_at_construction(self: @ContractState) -> u64 { + self.blk_nb.read() + } + // Gets the block timestamp + fn get_block_timestamp_at_construction(self: @ContractState) -> u64 { + self.blk_timestamp.read() + } + } + + fn assert_is_allowed_user() { + // checks if caller is '123' + let address = get_caller_address(); + assert(address.into() == 123, 'user is not allowed'); + } +} diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/cancel.cairo b/docs/listings/using_cheatcodes_cancelling_cheat/tests/lib.cairo similarity index 80% rename from docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/cancel.cairo rename to docs/listings/using_cheatcodes_cancelling_cheat/tests/lib.cairo index 2b550e0068..3024c98a43 100644 --- a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/cancel.cairo +++ b/docs/listings/using_cheatcodes_cancelling_cheat/tests/lib.cairo @@ -1,15 +1,13 @@ -//ANCHOR:first_half use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address, stop_cheat_caller_address }; -use using_cheatcodes::{ICheatcodeCheckerSafeDispatcher, ICheatcodeCheckerSafeDispatcherTrait}; +use using_cheatcodes_cancelling_cheat::{ + ICheatcodeCheckerSafeDispatcher, ICheatcodeCheckerSafeDispatcherTrait +}; #[test] -//ANCHOR_END:first_half -#[should_panic(expected: 'Second call failed!')] -//ANCHOR:second_half #[feature("safe_dispatcher")] fn call_and_invoke() { let contract = declare("CheatcodeChecker").unwrap().contract_class(); @@ -37,6 +35,3 @@ fn call_and_invoke() { let balance = dispatcher.get_balance(); assert_eq!(balance, Result::Ok(100)); } -//ANCHOR_END:second_half - -mod dummy {} // trick `scarb fmt -c` diff --git a/docs/listings/using_cheatcodes_cheat_address/Scarb.toml b/docs/listings/using_cheatcodes_cheat_address/Scarb.toml new file mode 100644 index 0000000000..b75776b9b3 --- /dev/null +++ b/docs/listings/using_cheatcodes_cheat_address/Scarb.toml @@ -0,0 +1,17 @@ +[package] +name = "using_cheatcodes_cheat_address" +version = "0.1.0" +edition = "2023_11" + +[dependencies] +starknet = "2.7.0" +assert_macros = "0.1.0" + +[dev-dependencies] +snforge_std = { path = "../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/docs/listings/using_cheatcodes_cheat_address/src/lib.cairo b/docs/listings/using_cheatcodes_cheat_address/src/lib.cairo new file mode 100644 index 0000000000..b6c20ffcad --- /dev/null +++ b/docs/listings/using_cheatcodes_cheat_address/src/lib.cairo @@ -0,0 +1,55 @@ +#[starknet::interface] +pub trait ICheatcodeChecker { + fn increase_balance(ref self: TContractState, amount: felt252); + fn get_balance(self: @TContractState) -> felt252; + fn get_block_number_at_construction(self: @TContractState) -> u64; + fn get_block_timestamp_at_construction(self: @TContractState) -> u64; +} + +#[starknet::contract] +pub mod CheatcodeChecker { + use core::box::BoxTrait; + use starknet::get_caller_address; + + #[storage] + struct Storage { + balance: felt252, + blk_nb: u64, + blk_timestamp: u64, + } + + #[constructor] + fn constructor(ref self: ContractState) { + // store the current block number + self.blk_nb.write(starknet::get_block_info().unbox().block_number); + // store the current block timestamp + self.blk_timestamp.write(starknet::get_block_info().unbox().block_timestamp); + } + + #[abi(embed_v0)] + impl ICheatcodeCheckerImpl of super::ICheatcodeChecker { + // Increases the balance by the given amount + fn increase_balance(ref self: ContractState, amount: felt252) { + assert_is_allowed_user(); + self.balance.write(self.balance.read() + amount); + } + // Gets the balance. + fn get_balance(self: @ContractState) -> felt252 { + self.balance.read() + } + // Gets the block number + fn get_block_number_at_construction(self: @ContractState) -> u64 { + self.blk_nb.read() + } + // Gets the block timestamp + fn get_block_timestamp_at_construction(self: @ContractState) -> u64 { + self.blk_timestamp.read() + } + } + + fn assert_is_allowed_user() { + // checks if caller is '123' + let address = get_caller_address(); + assert(address.into() == 123, 'user is not allowed'); + } +} diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/proper_use.cairo b/docs/listings/using_cheatcodes_cheat_address/tests/lib.cairo similarity index 87% rename from docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/proper_use.cairo rename to docs/listings/using_cheatcodes_cheat_address/tests/lib.cairo index cd4b71f24b..5c29b4d211 100644 --- a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/proper_use.cairo +++ b/docs/listings/using_cheatcodes_cheat_address/tests/lib.cairo @@ -1,5 +1,5 @@ use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address}; -use using_cheatcodes::{ICheatcodeCheckerDispatcher, ICheatcodeCheckerDispatcherTrait}; +use using_cheatcodes_cheat_address::{ICheatcodeCheckerDispatcher, ICheatcodeCheckerDispatcherTrait}; #[test] fn call_and_invoke() { diff --git a/docs/listings/snforge_advanced_features/Scarb.toml b/docs/listings/using_cheatcodes_others/Scarb.toml similarity index 59% rename from docs/listings/snforge_advanced_features/Scarb.toml rename to docs/listings/using_cheatcodes_others/Scarb.toml index ec83246478..f9049e22b8 100644 --- a/docs/listings/snforge_advanced_features/Scarb.toml +++ b/docs/listings/using_cheatcodes_others/Scarb.toml @@ -1,9 +1,13 @@ -[workspace] -members = ["crates/*"] +[package] +name = "using_cheatcodes_others" +version = "0.1.0" +edition = "2023_11" -[workspace.dependencies] +[dependencies] starknet = "2.7.0" assert_macros = "0.1.0" + +[dev-dependencies] snforge_std = { path = "../../../snforge_std" } [[target.starknet-contract]] diff --git a/docs/listings/using_cheatcodes_others/src/lib.cairo b/docs/listings/using_cheatcodes_others/src/lib.cairo new file mode 100644 index 0000000000..b6c20ffcad --- /dev/null +++ b/docs/listings/using_cheatcodes_others/src/lib.cairo @@ -0,0 +1,55 @@ +#[starknet::interface] +pub trait ICheatcodeChecker { + fn increase_balance(ref self: TContractState, amount: felt252); + fn get_balance(self: @TContractState) -> felt252; + fn get_block_number_at_construction(self: @TContractState) -> u64; + fn get_block_timestamp_at_construction(self: @TContractState) -> u64; +} + +#[starknet::contract] +pub mod CheatcodeChecker { + use core::box::BoxTrait; + use starknet::get_caller_address; + + #[storage] + struct Storage { + balance: felt252, + blk_nb: u64, + blk_timestamp: u64, + } + + #[constructor] + fn constructor(ref self: ContractState) { + // store the current block number + self.blk_nb.write(starknet::get_block_info().unbox().block_number); + // store the current block timestamp + self.blk_timestamp.write(starknet::get_block_info().unbox().block_timestamp); + } + + #[abi(embed_v0)] + impl ICheatcodeCheckerImpl of super::ICheatcodeChecker { + // Increases the balance by the given amount + fn increase_balance(ref self: ContractState, amount: felt252) { + assert_is_allowed_user(); + self.balance.write(self.balance.read() + amount); + } + // Gets the balance. + fn get_balance(self: @ContractState) -> felt252 { + self.balance.read() + } + // Gets the block number + fn get_block_number_at_construction(self: @ContractState) -> u64 { + self.blk_nb.read() + } + // Gets the block timestamp + fn get_block_timestamp_at_construction(self: @ContractState) -> u64 { + self.blk_timestamp.read() + } + } + + fn assert_is_allowed_user() { + // checks if caller is '123' + let address = get_caller_address(); + assert(address.into() == 123, 'user is not allowed'); + } +} diff --git a/docs/listings/using_cheatcodes_others/tests/caller_address.cairo b/docs/listings/using_cheatcodes_others/tests/caller_address.cairo new file mode 100644 index 0000000000..e8df5dd455 --- /dev/null +++ b/docs/listings/using_cheatcodes_others/tests/caller_address.cairo @@ -0,0 +1,2 @@ +pub mod proper_use_global; +pub mod span; diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/proper_use_global.cairo b/docs/listings/using_cheatcodes_others/tests/caller_address/proper_use_global.cairo similarity index 93% rename from docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/proper_use_global.cairo rename to docs/listings/using_cheatcodes_others/tests/caller_address/proper_use_global.cairo index b810ded3f0..e8f4262be1 100644 --- a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/proper_use_global.cairo +++ b/docs/listings/using_cheatcodes_others/tests/caller_address/proper_use_global.cairo @@ -2,7 +2,7 @@ use snforge_std::{ declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global }; -use using_cheatcodes::{ICheatcodeCheckerDispatcher, ICheatcodeCheckerDispatcherTrait}; +use using_cheatcodes_others::{ICheatcodeCheckerDispatcher, ICheatcodeCheckerDispatcherTrait}; #[test] fn call_and_invoke_global() { diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/span.cairo b/docs/listings/using_cheatcodes_others/tests/caller_address/span.cairo similarity index 93% rename from docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/span.cairo rename to docs/listings/using_cheatcodes_others/tests/caller_address/span.cairo index 0b05f181bf..eec19b9e60 100644 --- a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/span.cairo +++ b/docs/listings/using_cheatcodes_others/tests/caller_address/span.cairo @@ -1,7 +1,9 @@ use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, cheat_caller_address, CheatSpan}; use starknet::ContractAddress; -use using_cheatcodes::{ICheatcodeCheckerSafeDispatcher, ICheatcodeCheckerSafeDispatcherTrait}; +use using_cheatcodes_others::{ + ICheatcodeCheckerSafeDispatcher, ICheatcodeCheckerSafeDispatcherTrait +}; #[test] #[feature("safe_dispatcher")] diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/cheat_constructor.cairo b/docs/listings/using_cheatcodes_others/tests/cheat_constructor.cairo similarity index 91% rename from docs/listings/snforge_overview/crates/using_cheatcodes/tests/cheat_constructor.cairo rename to docs/listings/using_cheatcodes_others/tests/cheat_constructor.cairo index 829d269a35..fa39e238fb 100644 --- a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/cheat_constructor.cairo +++ b/docs/listings/using_cheatcodes_others/tests/cheat_constructor.cairo @@ -3,7 +3,7 @@ use snforge_std::{ start_cheat_block_timestamp }; -use using_cheatcodes::{ICheatcodeCheckerDispatcher, ICheatcodeCheckerDispatcherTrait}; +use using_cheatcodes_others::{ICheatcodeCheckerDispatcher, ICheatcodeCheckerDispatcherTrait}; #[test] fn call_and_invoke() { diff --git a/docs/listings/snforge_overview/crates/using_cheatcodes/tests/lib.cairo b/docs/listings/using_cheatcodes_others/tests/lib.cairo similarity index 100% rename from docs/listings/snforge_overview/crates/using_cheatcodes/tests/lib.cairo rename to docs/listings/using_cheatcodes_others/tests/lib.cairo diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 9ed93f8e27..aa294b1189 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -24,6 +24,8 @@ * [Testing Workspaces](testing/testing-workspaces.md) * [Test Collection](testing/test-collection.md) * [Contract Collection](testing/contracts-collection.md) + * [Optimized Mechanism](testing/contract-collection/new-mechanism.md) + * [Old Mechanism](testing/contract-collection/old-mechanism.md) * [Gas and VM Resources Estimation](testing/gas-and-resource-estimation.md) * [Coverage](testing/coverage.md) @@ -34,12 +36,13 @@ * [Conditional Compilation](snforge-advanced-features/conditional-compilation.md) * [Direct Storage Access](snforge-advanced-features/storage-cheatcodes.md) * [Profiling](snforge-advanced-features/profiling.md) +* [Backtrace](snforge-advanced-features/backtrace.md) --- # `sncast` Overview -* [Outline](starknet/index.md) +* [Outline](starknet/sncast-overview.md) * [Creating And Deploying Accounts](starknet/account.md) * [Importing Accounts](starknet/account-import.md) * [Declaring New Contracts](starknet/declare.md) diff --git a/docs/src/appendix/sncast-library/call.md b/docs/src/appendix/sncast-library/call.md index f943e458da..3e76894fe3 100644 --- a/docs/src/appendix/sncast-library/call.md +++ b/docs/src/appendix/sncast-library/call.md @@ -11,7 +11,7 @@ Calls a contract and returns `CallResult`. - `calldata` - inputs to the function to be called. ```rust -{{#include ../../../listings/sncast_library/scripts/call/src/lib.cairo}} +{{#include ../../../listings/call/src/lib.cairo}} ``` Structure used by the command: diff --git a/docs/src/appendix/sncast-library/declare.md b/docs/src/appendix/sncast-library/declare.md index 8165a69b2d..f75510507d 100644 --- a/docs/src/appendix/sncast-library/declare.md +++ b/docs/src/appendix/sncast-library/declare.md @@ -9,7 +9,7 @@ Declares a contract and returns `DeclareResult`. - `nonce` - nonce for declare transaction. If not provided, nonce will be set automatically. ```rust -{{#include ../../../listings/sncast_library/scripts/declare/src/lib.cairo}} +{{#include ../../../listings/declare/src/lib.cairo}} ``` ## Returned Type diff --git a/docs/src/appendix/sncast-library/deploy.md b/docs/src/appendix/sncast-library/deploy.md index 326d7de9fd..e503218118 100644 --- a/docs/src/appendix/sncast-library/deploy.md +++ b/docs/src/appendix/sncast-library/deploy.md @@ -46,5 +46,5 @@ pub struct StrkFeeSettings { - `nonce` - nonce for declare transaction. If not provided, nonce will be set automatically. ```rust -{{#include ../../../listings/sncast_library/scripts/deploy/src/lib.cairo}} +{{#include ../../../listings/deploy/src/lib.cairo}} ``` diff --git a/docs/src/appendix/sncast-library/get_nonce.md b/docs/src/appendix/sncast-library/get_nonce.md index 3261250bca..2ad4b84559 100644 --- a/docs/src/appendix/sncast-library/get_nonce.md +++ b/docs/src/appendix/sncast-library/get_nonce.md @@ -7,5 +7,5 @@ Gets nonce of an account for a given block tag (`pending` or `latest`) and retur - `block_tag` - block tag name, one of `pending` or `latest`. ```rust -{{#include ../../../listings/sncast_library/scripts/get_nonce/src/lib.cairo}} +{{#include ../../../listings/get_nonce/src/lib.cairo}} ``` diff --git a/docs/src/appendix/sncast-library/invoke.md b/docs/src/appendix/sncast-library/invoke.md index 8cd5c0a121..db964a11a0 100644 --- a/docs/src/appendix/sncast-library/invoke.md +++ b/docs/src/appendix/sncast-library/invoke.md @@ -17,7 +17,7 @@ Invokes a contract and returns `InvokeResult`. - `nonce` - nonce for declare transaction. If not provided, nonce will be set automatically. ```rust -{{#include ../../../listings/sncast_library/scripts/invoke/src/lib.cairo}} +{{#include ../../../listings/invoke/src/lib.cairo}} ``` Structures used by the command: diff --git a/docs/src/appendix/sncast-library/tx_status.md b/docs/src/appendix/sncast-library/tx_status.md index f00842679b..83536455b1 100644 --- a/docs/src/appendix/sncast-library/tx_status.md +++ b/docs/src/appendix/sncast-library/tx_status.md @@ -7,7 +7,7 @@ Gets the status of a transaction using its hash and returns `TxStatusResult`. - `transaction_hash` - hash of the transaction ```rust -{{#include ../../../listings/sncast_library/scripts/tx_status/src/lib.cairo}} +{{#include ../../../listings/tx_status/src/lib.cairo}} ``` Structures used by the command: diff --git a/docs/src/appendix/sncast/account/create.md b/docs/src/appendix/sncast/account/create.md index 01d1b895a1..f102c2424a 100644 --- a/docs/src/appendix/sncast/account/create.md +++ b/docs/src/appendix/sncast/account/create.md @@ -37,7 +37,7 @@ Salt for the account address. If omitted random one will be generated. ## `--add-profile ` Optional. -If passed, a profile with corresponding name will be added to snfoundry.toml. +If passed, a profile with corresponding name will be added to the local snfoundry.toml. ## `--class-hash, -c` Optional. diff --git a/docs/src/appendix/sncast/account/import.md b/docs/src/appendix/sncast/account/import.md index aa990f74de..e1ad3c0715 100644 --- a/docs/src/appendix/sncast/account/import.md +++ b/docs/src/appendix/sncast/account/import.md @@ -49,4 +49,4 @@ Salt for the account address. ## `--add-profile ` Optional. -If passed, a profile with corresponding name will be added to snfoundry.toml. +If passed, a profile with corresponding name will be added to the local snfoundry.toml. diff --git a/docs/src/appendix/snfoundry-toml.md b/docs/src/appendix/snfoundry-toml.md index 9f0847f90b..296c0f3aed 100644 --- a/docs/src/appendix/snfoundry-toml.md +++ b/docs/src/appendix/snfoundry-toml.md @@ -25,19 +25,19 @@ The `url` field specifies the address of RPC provider. url = "http://example.com" ``` -#### `accounts_file` +#### `accounts-file` -The `accounts_file` field specifies the path to a file containing account information. +The `accounts-file` field specifies the path to a file containing account information. If not provided, the default path is `~/.starknet_accounts/starknet_open_zeppelin_accounts.json`. ```toml [sncast.myprofile] -accounts_file = "path/to/accounts.json" +accounts-file = "path/to/accounts.json" ``` #### `account` -The `account` field specifies which account from the `accounts_file` to use for transactions. +The `account` field specifies which account from the `accounts-file` to use for transactions. ```toml [sncast.myprofile] @@ -53,19 +53,27 @@ The `keystore` field specifies the path to the keystore file. keystore = "path/to/keystore" ``` -#### `wait_params` +#### `wait-params` -The `wait_params` field defines the waiting parameters for transactions. By default, timeout (in seconds) is set to `300` and retry_interval (in seconds) to `5`. +The `wait-params` field defines the waiting parameters for transactions. By default, timeout (in seconds) is set to `300` and retry-interval (in seconds) to `5`. This means transactions will be checked every `5 seconds`, with a total of `60 attempts` before timing out. ```toml [sncast.myprofile] -wait_params = { timeout = 300, retry-interval = 5 } +wait-params = { timeout = 300, retry-interval = 5 } ``` -#### `block_explorer` +#### `show-explorer-links` +Enable printing links pointing to pages with transaction details in the chosen block explorer -The `block_explorer` field specifies the block explorer service used to display links to transaction details. +```toml +[sncast.myprofile] +show-explorer-links = true +``` + +#### `block-explorer` + +The `block-explorer` field specifies the block explorer service used to display links to transaction details. | Value | URL | |-----------|----------------------------------------| @@ -77,7 +85,7 @@ The `block_explorer` field specifies the block explorer service used to display ```toml [sncast.myprofile] -block_explorer = "StarkScan" +block-explorer = "StarkScan" ``` #### Complete Example of `snfoundry.toml` File @@ -85,11 +93,12 @@ block_explorer = "StarkScan" ```toml [sncast.myprofile1] url = "http://127.0.0.1:5050/" -accounts_file = "../account-file" +accounts-file = "../account-file" account = "mainuser" keystore = "~/keystore" -wait_params = { timeout = 500, retry_interval = 10 } -block_explorer = "StarkScan" +wait-params = { timeout = 500, retry-interval = 10 } +block-explorer = "StarkScan" +show-explorer-links = true [sncast.dev] url = "http://127.0.0.1:5056/rpc" diff --git a/docs/src/getting-started/first-steps.md b/docs/src/getting-started/first-steps.md index c59dcbfd29..8aa0231c48 100644 --- a/docs/src/getting-started/first-steps.md +++ b/docs/src/getting-started/first-steps.md @@ -6,13 +6,13 @@ We demonstrate how to create a new project, compile, and test it. To start a new project with Starknet Foundry, run `snforge init` ```shell -$ snforge init project_name +$ snforge init hello_starknet ``` Let's check out the project structure ```shell -$ cd project_name +$ cd hello_starknet $ tree . -L 1 ``` @@ -23,10 +23,11 @@ $ tree . -L 1 . ├── Scarb.lock ├── Scarb.toml +├── snfoundry.toml ├── src └── tests -2 directories, 2 files +2 directories, 3 files ```

@@ -46,15 +47,12 @@ $ snforge test Output: ```shell - Compiling project_name v0.1.0 (project_name/Scarb.toml) - Finished release target(s) in 1 second - -Collected 2 test(s) from project_name package +Collected 2 test(s) from hello_starknet package Running 0 test(s) from src/ Running 2 test(s) from tests/ -[PASS] tests::test_contract::test_increase_balance (gas: ~170) -[PASS] tests::test_contract::test_cannot_increase_balance_with_zero_value (gas: ~104) -Tests: 2 passed, 0 failed, 0 skipped, 0 ignored +[PASS] hello_starknet_integrationtest::test_contract::test_cannot_increase_balance_with_zero_value (gas: ~105) +[PASS] hello_starknet_integrationtest::test_contract::test_increase_balance (gas: ~172) +Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ```

@@ -75,6 +73,7 @@ snforge_std = "0.33.0" Make sure that the above version matches the installed `snforge` version. You can check the currently installed version with + ```shell $ snforge --version ``` diff --git a/docs/src/projects/configuration.md b/docs/src/projects/configuration.md index 45638a5e62..51b6580bfe 100644 --- a/docs/src/projects/configuration.md +++ b/docs/src/projects/configuration.md @@ -54,7 +54,7 @@ $ sncast --profile myprofile \ call \ --contract-address 0x38b7b9507ccf73d79cb42c2cc4e58cf3af1248f342112879bfdf5aa4f606cc9 \ --function get \ - --calldata 0x0 \ + --arguments '0' \ --block-id latest ``` @@ -91,7 +91,7 @@ With this, there's no need to include the `--profile` argument when using `sncas $ sncast call \ --contract-address 0x38b7b9507ccf73d79cb42c2cc4e58cf3af1248f342112879bfdf5aa4f606cc9 \ --function get \ - --calldata 0x0 \ + --arguments '0' \ --block-id latest ``` @@ -105,7 +105,62 @@ response: [0x1, 0x23, 0x4]

-## Environmental variables +### Global Configuration + +Global configuration file is a [`snfoundry.toml`](https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html), +which is a common storage for configurations to apply to multiple projects across various directories. +This file is stored in a predefined location and is used to store profiles that can be used from any location on your computer. + +#### Interaction Between Local and Global Profiles + +Global config can be overridden by a local config. + +If both local and global profiles with the same name are present, local profile will be combined with global profile. For any setting defined in both profiles, the local setting will take precedence. For settings not defined in the local profile, values from the corresponding global profile will be used, or if not defined, values from the global default profile will be used instead. + +This same behavior applies for [default profiles](#default-profile) as well. A local default profile will override a global default profile. + +> 📝 **Note** +> Remember that arguments passed in the CLI have the highest priority and will always override the configuration file settings. + + +#### Global Configuration File Location +The global configuration is stored in a specific location depending on the operating system: + +- macOS/Linux : The global configuration file is located at `$HOME/.config/starknet-foundry/snfoundry.toml` +- Windows : The file can be found at `C:\Users\\AppData\Roaming\starknet-foundry\snfoundry.toml` + +> 📝 **Note** +> If missing, global configuration file will be created automatically on running any `sncast` command for the first time. + +### Config Interaction Example + +``` +root/ +├── .config/ +│ └── starknet-foundry/ +│ └── snfoundry.toml -> A +└── /../../ + └── projects/ + ├── snfoundry.toml -> B + └── cairo-projects/ + └── opus-magnum/ +``` + +**Glossary:** + +- **A:** Global configuration file containing the profiles [`default`](#default-profile) and [`testnet`](#defining-profiles-in-snfoundrytoml). +- **B:** Local configuration file containing the profiles `default` and `mainnet`. + +In any directory in the file system, a user can run the `sncast` command using the `default` and `testnet` profiles, +because they are defined in global config (file A). + +If no profiles are explicitly specified, the `default` profile from the global configuration file will be used. + +When running `sncast` from the `opus-magnum` directory, there is a configuration file in the parent directory (file B). +This setup allows for the use of the following profiles: `default`, `testnet`, and `mainnet`. If the `mainnet` profile is specified, +the configuration from the local file will be used to override the global `default` profile, as the `mainnet` profile does not exist in the global configuration. + +## Environmental Variables Programmers can use environmental variables in both `Scarb.toml::tool::snforge` and in `snfoundry.toml`. To use an environmental variable as a value, use its name prefixed with `$`. This might be useful, for example, to hide node urls in the public repositories. @@ -120,4 +175,4 @@ url = "$NODE_URL" # ... ``` -Variable value are automatically resolved to numbers and booleans (strings `true`, `false`) if it is possible. +Variable values are automatically resolved to numbers and booleans (strings `true`, `false`) where possible. diff --git a/docs/src/snforge-advanced-features/backtrace.md b/docs/src/snforge-advanced-features/backtrace.md new file mode 100644 index 0000000000..6075567d94 --- /dev/null +++ b/docs/src/snforge-advanced-features/backtrace.md @@ -0,0 +1,91 @@ +# Backtrace + +## Prerequisites + +Backtrace feature relies on debug information provided by Scarb. To generate the necessary debug information, you need +to have: + +1. [Scarb](https://github.com/software-mansion/scarb) version `2.8.0` or higher +2. `Scarb.toml` file with the following Cairo compiler configuration: + +```toml +[profile.dev.cairo] +unstable-add-statements-code-locations-debug-info = true +unstable-add-statements-functions-debug-info = true +``` + +## Usage + +> 📝 **Note** +> Currently, only the last line of failure in each contract is guaranteed to appear in the backtrace. The complete call +> tree is not fully supported yet; however, in most cases, it will be available. It internally relies on the inlining +> behavior of the compiler, and a full backtrace is available if all functions are inlined. To obtain a more detailed +> backtrace, ensure that +> your [inlining strategy](https://docs.swmansion.com/scarb/docs/reference/manifest.html#inlining-strategy) in +`Scarb.toml` is set to `default`. + +When a contract call fails, the error message alone may not always provide enough information to identify the root cause +of the issue. To aid in debugging, `snforge` offers a feature that can generate a backtrace of the execution. + +If your contract fails and a backtrace can be generated, `snforge` will prompt you to run the operation again with the +`SNFORGE_BACKTRACE=1` environment variable (if it’s not already configured). For example, you may see failure data like +this: + + + + + +```shell +$ snforge test +``` +
+Output: + +```shell +Failure data: + (0x454e545259504f494e545f4e4f545f464f554e44 ('ENTRYPOINT_NOT_FOUND'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) +note: run with `SNFORGE_BACKTRACE=1` environment variable to display a backtrace +``` +
+
+ + +To enable backtraces, simply set the `SNFORGE_BACKTRACE=1` environment variable and rerun the operation. + +When enabled, the backtrace will display the call tree of the execution, including the specific line numbers in the +contracts where the errors occurred. Here's an example of what you might see: + + + + +```shell +$ SNFORGE_BACKTRACE=1 snforge test +``` +
+Output: + +```shell +Failure data: + (0x454e545259504f494e545f4e4f545f464f554e44 ('ENTRYPOINT_NOT_FOUND'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')) + +Error occurred in contract 'InnerContract' at pc: '72' +Stack backtrace: + 0: backtrace_vm_error::InnerContract::inner_call + at [..]/src/lib.cairo:47:9 + 1: backtrace_vm_error::InnerContract::InnerContract::inner + at [..]/src/lib.cairo:38:13 + 2: backtrace_vm_error::InnerContract::__wrapper__InnerContract__inner + at [..]/src/lib.cairo:37:9 + +Error occurred in contract 'OuterContract' at pc: '107' +Stack backtrace: + 0: backtrace_vm_error::IInnerContractDispatcherImpl::inner + at [..]/src/lib.cairo:22:1 + 1: backtrace_vm_error::OuterContract::OuterContract::outer + at [..]/src/lib.cairo:17:13 + 2: backtrace_vm_error::OuterContract::__wrapper__OuterContract__outer + at [..]/src/lib.cairo:15:9 +``` +
+
+ diff --git a/docs/src/snforge-advanced-features/conditional-compilation.md b/docs/src/snforge-advanced-features/conditional-compilation.md index 28cdc2ba7f..459298b1e5 100644 --- a/docs/src/snforge-advanced-features/conditional-compilation.md +++ b/docs/src/snforge-advanced-features/conditional-compilation.md @@ -16,7 +16,7 @@ Additionally, for utilizing features the `snforge test` command exposes the foll Firstly, define a contract in the `src` directory with a `#[cfg(feature: '')]` attribute: ```rust -{{#include ../../listings/snforge_advanced_features/crates/conditional_compilation/src/lib.cairo}} +{{#include ../../listings/conditional_compilation/src/lib.cairo}} ``` > 📝 **Note** @@ -27,7 +27,7 @@ Firstly, define a contract in the `src` directory with a `#[cfg(feature: 'Output:
```shell -Collected 1 test(s) from fuzz_testing package -Running 1 test(s) from src/ -Running 0 test(s) from tests/ -[PASS] fuzz_testing::basic_example::test_sum (runs: 256, gas: {max: ~1, min: ~1, mean: ~1.00, std deviation: ~0.00}) -Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out +Collected 2 test(s) from fuzz_testing package +Running 2 test(s) from src/ +[PASS] fuzz_testing::with_parameters::tests::test_sum (runs: 22, gas: {max: ~1, min: ~1, mean: ~1.00, std deviation: ~0.00}) +[PASS] fuzz_testing::basic_example::tests::test_sum (runs: 256, gas: {max: ~1, min: ~1, mean: ~1.00, std deviation: ~0.00}) +Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out Fuzzer seed: [..] ```
@@ -59,7 +59,7 @@ Trying to use arguments of different type in test definition will result in an e It is possible to configure the number of runs of the random fuzzer as well as its seed for a specific test case: ```rust -{{#include ../../listings/snforge_advanced_features/crates/fuzz_testing/src/with_parameters.cairo}} +{{#include ../../listings/fuzz_testing/src/with_parameters.cairo}} ``` It can also be configured globally, via command line arguments: diff --git a/docs/src/snforge-advanced-features/storage-cheatcodes.md b/docs/src/snforge-advanced-features/storage-cheatcodes.md index 7127d92dd3..d82a2d9e41 100644 --- a/docs/src/snforge-advanced-features/storage-cheatcodes.md +++ b/docs/src/snforge-advanced-features/storage-cheatcodes.md @@ -15,13 +15,13 @@ This example uses only felts for simplicity. 1. Exact storage fields ```rust -{{#include ../../listings/snforge_advanced_features/crates/direct_storage_access/tests/felts_only/field.cairo}} +{{#include ../../listings/direct_storage_access/tests/felts_only/field.cairo}} ``` 2. Map entries ```rust -{{#include ../../listings/snforge_advanced_features/crates/direct_storage_access/tests/felts_only/map_entry.cairo}} +{{#include ../../listings/direct_storage_access/tests/felts_only/map_entry.cairo}} ``` ## Example: Complex structures in storage @@ -30,13 +30,13 @@ This example uses a complex key and value, with default derived serialization me We use a contract along with helper structs: ```rust -{{#include ../../listings/snforge_advanced_features/crates/direct_storage_access/src/complex_structures.cairo}} +{{#include ../../listings/direct_storage_access/src/complex_structures.cairo}} ``` And perform a test checking `load` and `store` behavior in context of those structs: ```rust -{{#include ../../listings/snforge_advanced_features/crates/direct_storage_access/tests/complex_structures.cairo}} +{{#include ../../listings/direct_storage_access/tests/complex_structures.cairo}} ``` > ⚠️ **Warning** @@ -49,3 +49,10 @@ And perform a test checking `load` and `store` behavior in context of those stru > 📝 **Note** > > The `load` cheatcode will return zeros for memory you haven't written into yet (it is a default storage value for Starknet contracts' storage). + +## Example with `storage_address_from_base` +This example uses `storage_address_from_base` with entry's of the storage variable. + +```rust +{{#include ../../listings/snforge_advanced_features/crates/direct_storage_access/tests/using_storage_address_from_base.cairo}} +``` \ No newline at end of file diff --git a/docs/src/starknet/account-import.md b/docs/src/starknet/account-import.md index 1451365abb..31039fe0ae 100644 --- a/docs/src/starknet/account-import.md +++ b/docs/src/starknet/account-import.md @@ -87,6 +87,7 @@ $ sncast \ If you don't want to pass the private key in the command (because of safety aspect), you can skip `--private-key` flag. You will be prompted to enter the private key in interactive mode. + ```shell $ sncast \ account import \ diff --git a/docs/src/starknet/account.md b/docs/src/starknet/account.md index 79fbf962a9..517e30e62a 100644 --- a/docs/src/starknet/account.md +++ b/docs/src/starknet/account.md @@ -61,11 +61,12 @@ You can do it both by sending tokens from another starknet account or by bridgin ```shell $ sncast \ - account deploy \ + account deploy \ --url http://127.0.0.1:5050 \ --name some-name \ --fee-token strk \ --max-fee 9999999999999 +```
Output: @@ -150,6 +151,7 @@ $ sncast \ --name some-name \ --url http://127.0.0.1:5050 \ --class-hash 0x00e2eb8f5672af4e6a4e8a8f1b44989685e668489b0a25437733756c5a34a1d6 + --type oz ``` #### [`account create`](../appendix/sncast/account/create.md) With Salt Argument diff --git a/docs/src/starknet/call.md b/docs/src/starknet/call.md index 17d1cb3c82..541c297f5e 100644 --- a/docs/src/starknet/call.md +++ b/docs/src/starknet/call.md @@ -20,9 +20,9 @@ For a detailed CLI description, see the [call command reference](../appendix/snc $ sncast \ call \ --url http://127.0.0.1:5050 \ - --contract-address 0x4a739ab73aa3cac01f9da5d55f49fb67baee4919224454a2e3f85b16462a911 \ - --function "some_function" \ - --calldata 1 2 3 + --contract-address 0x522dc7cbe288037382a02569af5a4169531053d284193623948eac8dd051716 \ + --function "balance_of" \ + --arguments '0x0554d15a839f0241ba465bb176d231730c01cf89cdcb95fe896c51d4a6f4bb8f' ```
@@ -30,7 +30,7 @@ $ sncast \ ```shell command: call -response: [0x1, 0x23, 0x4] +response: [0x1, 0x0] ```

@@ -44,10 +44,11 @@ You can call a contract at the specific block by passing `--block-id` argument. ```shell $ sncast call \ - --contract-address 0x4a739ab73aa3cac01f9da5d55f49fb67baee4919224454a2e3f85b16462a911 \ - --function "some_function" \ - --calldata 1 2 3 \ - --block-id 1234 + --url http://127.0.0.1:5050 \ + --contract-address 0x522dc7cbe288037382a02569af5a4169531053d284193623948eac8dd051716 \ + --function "balance_of" \ + --arguments '0x0554d15a839f0241ba465bb176d231730c01cf89cdcb95fe896c51d4a6f4bb8f' \ + --block-id 77864 ```
@@ -55,6 +56,6 @@ $ sncast call \ ```shell command: call -response: [0x1, 0x23] +response: [0x0, 0x0] ```
diff --git a/docs/src/starknet/invoke.md b/docs/src/starknet/invoke.md index d7a642018d..10bd551526 100644 --- a/docs/src/starknet/invoke.md +++ b/docs/src/starknet/invoke.md @@ -21,10 +21,13 @@ $ sncast \ --account example_user \ invoke \ --url http://127.0.0.1:5050 \ - --fee-token strk \ - --contract-address 0x4a739ab73aa3cac01f9da5d55f49fb67baee4919224454a2e3f85b16462a911 \ - --function "some_function" \ - --calldata 1 2 0x1e + --contract-address 0x522dc7cbe288037382a02569af5a4169531053d284193623948eac8dd051716 \ + --function "add" \ + --fee-token eth \ + --arguments 'pokemons::model::PokemonData {'\ +'name: "Magmar",'\ +'element: pokemons::model::Element::Fire'\ +'}' ```
@@ -32,10 +35,10 @@ $ sncast \ ```shell command: invoke -transaction_hash: 0x7ad0d6e449e33b6581a4bb8df866c0fce3919a5ee05a30840ba521dafee217f +transaction_hash: 0x504f830428d0fcf462b4b814e2f67e12dfbcf3dc7847c1e36ba39d3eb7ac313 To see invocation details, visit: -transaction: https://starkscan.co/tx/0x7ad0d6e449... +transaction: https://sepolia.starkscan.co/tx/0x504f830428d0fcf462b4b814e2f67e12dfbcf3dc7847c1e36ba39d3eb7ac313 ```

diff --git a/docs/src/starknet/script.md b/docs/src/starknet/script.md index fecce2ed10..d822d4dda0 100644 --- a/docs/src/starknet/script.md +++ b/docs/src/starknet/script.md @@ -221,7 +221,7 @@ For more details, see [init command](../appendix/sncast/script/init.md). This example shows how to call an already deployed contract. Please find full example with contract deployment [here](#full-example-with-contract-deployment). ```rust -{{#include ../../listings/sncast_overview/scripts/basic_example/src/basic_example.cairo}} +{{#include ../../listings/basic_example/src/basic_example.cairo}} ``` The script should be included in a Scarb package. The directory structure and config for this example looks like this: @@ -276,13 +276,13 @@ status: success This example script declares, deploys and interacts with an example `MapContract`: ```rust -{{#include ../../listings/sncast_overview/crates/map3/src/lib.cairo}} +{{#include ../../listings/map3/src/lib.cairo}} ``` We prepare a script: ```rust -{{#include ../../listings/sncast_overview/scripts/full_example/src/full_example.cairo}} +{{#include ../../listings/full_example/src/full_example.cairo}} ``` The script should be included in a Scarb package. The directory structure and config for this example looks like this: @@ -414,7 +414,7 @@ Script errors implement `Debug` trait, allowing the error to be printed to stdou ### Minimal example with `assert!` and `println!` ```rust -{{#include ../../listings/sncast_overview/scripts/error_handling/src/error_handling.cairo}} +{{#include ../../listings/error_handling/src/error_handling.cairo}} ``` More on deployment scripts errors [here](../appendix/sncast-library/errors.md). diff --git a/docs/src/starknet/index.md b/docs/src/starknet/sncast-overview.md similarity index 73% rename from docs/src/starknet/index.md rename to docs/src/starknet/sncast-overview.md index 32f98b060a..95602ff2ca 100644 --- a/docs/src/starknet/index.md +++ b/docs/src/starknet/sncast-overview.md @@ -11,6 +11,8 @@ Starknet Foundry `sncast` is a command line tool for performing Starknet RPC cal ## How to Use `sncast` To use `sncast`, run the `sncast` command followed by a subcommand (see [available commands](../appendix/sncast.md)): + + ```shell $ sncast ``` @@ -28,12 +30,11 @@ You can, however, overwrite their values by supplying them as flags directly to Let's use `sncast` to call a contract's function: ```shell -$ sncast --account myuser \ - call \ +$ sncast call \ --url http://127.0.0.1:5050 \ - --contract-address 0x38b7b9507ccf73d79cb42c2cc4e58cf3af1248f342112879bfdf5aa4f606cc9 \ - --function get \ - --calldata 0x0 \ + --contract-address 0x522dc7cbe288037382a02569af5a4169531053d284193623948eac8dd051716 \ + --function "pokemon" \ + --arguments '"Charizard"' \ --block-id latest ``` @@ -42,7 +43,7 @@ $ sncast --account myuser \ ```shell command: call -response: [0x0] +response: [0x0, 0x0, 0x43686172697a617264, 0x9, 0x0, 0x0, 0x41a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf] ```

@@ -51,21 +52,30 @@ response: [0x0] > In the above example we supply `sncast` with `--account` and `--url` flags. If `snfoundry.toml` is present, and have these properties set, values provided using these flags will override values from `snfoundry.toml`. Learn more about `snfoundry.toml` configuration [here](../projects/configuration.md#sncast). -### Calldata - -Some `sncast` commands (namely `call`, `deploy` and `invoke`) allow passing *calldata* - a series of arguments to perform an action with on blockchain. +### Arguments -In the example above we called a function with an argument: `0x0`, passed using `--calldata` flag. +Some `sncast` commands (namely `call`, `deploy` and `invoke`) allow passing arguments to perform an action with on the blockchain. -Please note the notation of the argument. The default way of passing calldata is a list of hexadecimally encoded field elements - the *serialized* calldata. -To obtain the serialized form of the wished data, one must write a Cairo program calling `Serde::serialize` on subsequent arguments and displaying the results. +Under the hood cast always send request with serialized form of arguments, but it can be passed in +human-readable form thanks to the [calldata transformation](./calldata-transformation.md) feature present in Cast. -It is also possible to pass calldata in more friendly, human readable form thanks to the [calldata transformation](./calldata-transformation.md) feature present in Cast. +In the example above we called a function with a deserialized argument: `'"Charizard"'`, passed using `--arguments` flag. > ⚠️ **Warning** > Cast will not verify the serialized calldata. Any errors caused by passing improper calldata in a serialized form will originate from the network. > Basic static analysis is possible only when passing expressions - see [calldata transformation](./calldata-transformation.md). + +### Using Serialized Calldata + +The same result can be achieved by passing serialized calldata, which is a list of hexadecimal-encoded field elements. + +For example, this is equivalent to using the --calldata option with the following value: 0x0 0x43686172697a617264 0x9. + +To obtain the serialized form of the wished data, you can write a Cairo program that calls `Serde::serialize` on subsequent arguments and displays the results. + +Read more about it in the [Cairo documentation](https://book.cairo-lang.org/appendix-03-derivable-traits.html?highlight=seri#serializing-with-serde). + ### How to Use `--wait` Flag Let's invoke a transaction and wait for it to be `ACCEPTED_ON_L2`. diff --git a/docs/src/testing/contract-collection/new-mechanism.md b/docs/src/testing/contract-collection/new-mechanism.md new file mode 100644 index 0000000000..350ddeae58 --- /dev/null +++ b/docs/src/testing/contract-collection/new-mechanism.md @@ -0,0 +1,39 @@ +# How Contracts Are Collected + +For the [`declare`](../../appendix/snforge-library/declare.md) to work, snforge must collect and call build on +contracts in the package. By default, if using Scarb version >= 2.8.3, snforge will combine test +collection and contract collection steps. + +When running `snforge test`, snforge will, under the hood, call the `scarb build --test` command. This command builds +all the test and contracts along them. Snforge collects these contracts and makes them available for declaring in tests. + +Contracts are collected from both `src` and `tests` directory, including modules marked with `#[cfg(test)]`. +Internally, snforge collects contracts from all `[[test]]` targets compiled by Scarb. +You can read more about that in [test collection](../test-collection.md) documentation. + +## Collection Order + +When multiple `[[test]]` targets are present, snforge will first try to collect contracts from `integration` `test-type` +target. If `integration` is not present, snforge will first collect contracts from the first encountered `[[test]]` +target. + +After collecting from initial `[[test]]` target, snforge will collect contracts from any other encountered targets. +No specific order of collection is guaranteed. + +> 📝 **Note** +> +> If multiple contracts with the same name are present, snforge will use the first encountered implementation and will +> not collect others. + +## Using External Contracts in Tests + +To use contract from dependencies in tests, `Scarb.toml` must be updated to include these contracts under +`[[target.starknet-contract]]`. + +```toml +[[target.starknet-contract]] +build-external-contracts = ["path::to::Contract1", "other::path::to::Contract2"] +``` + +For more information about `build-external-contracts`, +see [Scarb documentation](https://docs.swmansion.com/scarb/docs/extensions/starknet/contract-target.html#compiling-external-contracts). diff --git a/docs/src/testing/contract-collection/old-mechanism.md b/docs/src/testing/contract-collection/old-mechanism.md new file mode 100644 index 0000000000..af5725bcde --- /dev/null +++ b/docs/src/testing/contract-collection/old-mechanism.md @@ -0,0 +1,32 @@ +# How Contracts Are Collected + +When you call `snforge test`, one of the things that `snforge` does is that it calls Scarb, particularly `scarb build`. +It makes Scarb build all contracts from your package and save them to the `target/{current_profile}` directory +(read more on [Scarb website](https://docs.swmansion.com/scarb/docs/extensions/starknet/contract-target.html)). + +Then, `snforge` loads compiled contracts from the package your tests are located, allowing you to declare the contracts +in +tests. + +Only contracts from `src/` directory are loaded. Contracts from `/tests` and modules marked with `#[cfg(test)]` are not +build or collected. To create contracts to be specifically used in tests +see [conditional compilation](../../snforge-advanced-features/conditional-compilation.md). + +> ⚠️ **Warning** +> +> Make sure to define `[[target.starknet-contract]]` section in your `Scarb.toml`, otherwise Scarb won't build your +> contracts. + +## Using External Contracts In Tests + +If you wish to use contracts from your dependencies inside your tests (e.g. an ERC20 token, an account contract), +you must first make Scarb build them. You can do that by using `build-external-contracts` key in `Scarb.toml`, +e.g.: + +```toml +[[target.starknet-contract]] +build-external-contracts = ["openzeppelin::account::account::Account"] +``` + +For more information about `build-external-contracts`, +see [Scarb documentation](https://docs.swmansion.com/scarb/docs/extensions/starknet/contract-target.html#compiling-external-contracts). diff --git a/docs/src/testing/contracts-collection.md b/docs/src/testing/contracts-collection.md index 30477bea85..811af548c6 100644 --- a/docs/src/testing/contracts-collection.md +++ b/docs/src/testing/contracts-collection.md @@ -1,24 +1,25 @@ -## How Contracts Are Collected +# How Contracts Are Collected -When you call `snforge test`, one of the things that `snforge` does is that it calls Scarb, particularly `scarb build`. -It makes Scarb build all contracts from your package and save them to the `target/{current_profile}` directory -(read more on [Scarb website](https://docs.swmansion.com/scarb/docs/extensions/starknet/contract-target.html)). +`snforge` supports two mechanisms for collecting contracts used in tests. +The default one depends on Scarb version used and can be controlled with `--no-optimization` flag. -Then, `snforge` loads compiled contracts from the package your tests are in, allowing you to declare the contracts in tests. +- If using Scarb version >= 2.8.3, [optimized collection mechanism](contract-collection/new-mechanism.md) is used by + default. +- If using Scarb version < 2.8.3 or running `snforge test` with `--no-optimization` flag, + the [old collection mechanism](contract-collection/old-mechanism.md) is used. -> ⚠️ **Warning** +> 📝 **Note** > -> Make sure to define `[[target.starknet-contract]]` section in your `Scarb.toml`, otherwise Scarb won't build your contracts. - - -## Using External Contracts In Tests - -If you wish to use contracts from your dependencies inside your tests (e.g. an ERC20 token, an account contract), -you must first make Scarb build them. You can do that by using `build-external-contracts` property in `Scarb.toml`, e.g.: - -```toml -[[target.starknet-contract]] -build-external-contracts = ["openzeppelin::account::account::Account"] -``` - -For more information about `build-external-contracts`, see [Scarb documentation](https://docs.swmansion.com/scarb/docs/extensions/starknet/contract-target.html#compiling-external-contracts). +> Enabling new mechanism **requires** Scarb version >= 2.8.3. + +## Differences Between Collection Mechanisms + +| Feature | Old Mechanism | Optimised Mechanism | +|---------------------------------------------------------|---------------|---------------------| +| Using contracts from `/src` | ✅ | ✅ | +| Using contracts from `/tests` | ❌ | ✅ | +| Using contracts from modules marked with `#[cfg(test)]` | ❌ | ✅ | +| Using contracts from dependencies | ✅ | ✅ | +| Contracts more closely resemble ones from real network | ✅ | ❌ | +| Less compilation steps required (faster compilation) | ❌ | ✅ | +| Additional compilation step required (`scarb build`) | ✅ | ❌ | diff --git a/docs/src/testing/contracts.md b/docs/src/testing/contracts.md index 08e600fb4f..d47f0f2fb0 100644 --- a/docs/src/testing/contracts.md +++ b/docs/src/testing/contracts.md @@ -19,24 +19,24 @@ writing smart contracts, you often want to test their interactions with the bloc Let's consider a simple smart contract with two methods. ```rust -{{#include ../../listings/snforge_overview/crates/testing_smart_contracts/src/simple_contract.cairo}} +{{#include ../../listings/testing_smart_contracts_writing_tests/src/lib.cairo}} ``` Note that the name after `mod` will be used as the contract name for testing purposes. ## Writing Tests -Let's write a test that will deploy the `HelloStarknet` contract and call some functions. +Let's write a test that will deploy the `SimpleContract` contract and call some functions. ```rust -{{#include ../../listings/snforge_overview/crates/testing_smart_contracts/tests/simple_contract.cairo}} +{{#include ../../listings/testing_smart_contracts_writing_tests/tests/simple_contract.cairo}} ``` > 📝 **Note** > > Notice that the arguments to the contract's constructor (the `deploy`'s `calldata` argument) need to be serialized with `Serde`. > -> `HelloStarknet` contract has no constructor, so the calldata remains empty in the example above. +> `SimpleContract` contract has no constructor, so the calldata remains empty in the example above. ```shell $ snforge test @@ -46,11 +46,19 @@ $ snforge test Output: ```shell -Collected 1 test(s) from testing_smart_contracts package +Collected 2 test(s) from testing_smart_contracts_handling_errors package +Running 2 test(s) from tests/ +[FAIL] testing_smart_contracts_handling_errors_integrationtest::panic::failing + +Failure data: + (0x50414e4943 ('PANIC'), 0x444159544148 ('DAYTAH')) + +[PASS] testing_smart_contracts_handling_errors_integrationtest::handle_panic::handling_string_errors (gas: ~103) Running 0 test(s) from src/ -Running 1 test(s) from tests/ -[PASS] tests::call_and_invoke -Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out +Tests: 1 passed, 1 failed, 0 skipped, 0 ignored, 0 filtered out + +Failures: + testing_smart_contracts_handling_errors_integrationtest::panic::failing ```
@@ -64,14 +72,14 @@ panicking. First, let's add a new, panicking function to our contract. ```rust -{{#include ../../listings/snforge_overview/crates/testing_smart_contracts/src/handling_errors.cairo}} +{{#include ../../listings/testing_smart_contracts_handling_errors/src/lib.cairo}} ``` If we called this function in a test, it would result in a failure. ```rust -{{#include ../../listings/snforge_overview/crates/testing_smart_contracts/tests/panic.cairo:first_half}} -{{#include ../../listings/snforge_overview/crates/testing_smart_contracts/tests/panic.cairo:second_half}} +{{#include ../../listings/testing_smart_contracts_handling_errors/tests/panic.cairo:first_half}} +{{#include ../../listings/testing_smart_contracts_handling_errors/tests/panic.cairo:second_half}} ``` ```shell @@ -82,18 +90,19 @@ $ snforge test Output: ```shell -Collected 1 test(s) from testing_smart_contracts package -Running 0 test(s) from src/ -Running 1 test(s) from tests/ -[FAIL] tests::failing +Collected 2 test(s) from testing_smart_contracts_handling_errors package +Running 2 test(s) from tests/ +[FAIL] testing_smart_contracts_handling_errors_integrationtest::panic::failing Failure data: (0x50414e4943 ('PANIC'), 0x444159544148 ('DAYTAH')) -Tests: 0 passed, 1 failed, 0 skipped, 0 ignored, 0 filtered out +[PASS] testing_smart_contracts_handling_errors_integrationtest::handle_panic::handling_string_errors (gas: ~103) +Running 0 test(s) from src/ +Tests: 1 passed, 1 failed, 0 skipped, 0 ignored, 0 filtered out Failures: - tests::failing + testing_smart_contracts_handling_errors_integrationtest::panic::failing ```
@@ -107,7 +116,7 @@ but are available for testing purposes. They allow using the contract without automatically unwrapping the result, which allows to catch the error like shown below. ```rust -{{#include ../../listings/snforge_overview/crates/testing_smart_contracts/tests/safe_dispatcher.cairo}} +{{#include ../../listings/testing_smart_contracts_safe_dispatcher/tests/safe_dispatcher.cairo}} ``` Now the test passes as expected. @@ -120,10 +129,10 @@ $ snforge test Output: ```shell -Collected 1 test(s) from package_name package +Collected 1 test(s) from testing_smart_contracts_safe_dispatcher package Running 0 test(s) from src/ Running 1 test(s) from tests/ -[PASS] tests::handling_errors +[PASS] testing_smart_contracts_safe_dispatcher_integrationtest::safe_dispatcher::handling_errors (gas: ~103) Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ``` @@ -132,7 +141,7 @@ Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out Similarly, you can handle the panics which use `ByteArray` as an argument (like an `assert!` or `panic!` macro) ```rust -{{#include ../../listings/snforge_overview/crates/testing_smart_contracts/tests/handle_panic.cairo}} +{{#include ../../listings/testing_smart_contracts_handling_errors/tests/handle_panic.cairo}} ``` You also could skip the de-serialization of the `panic_data`, and not use `try_deserialize_bytearray_error`, but this way you can actually use assertions on the `ByteArray` that was used to panic. diff --git a/docs/src/testing/gas-and-resource-estimation.md b/docs/src/testing/gas-and-resource-estimation.md index 0529400270..af5a4d6111 100644 --- a/docs/src/testing/gas-and-resource-estimation.md +++ b/docs/src/testing/gas-and-resource-estimation.md @@ -44,13 +44,22 @@ $ snforge test --detailed-resources Output: ```shell -... -[PASS] package_name::tests::resources (gas: ~2213) - steps: 881 - memory holes: 36 - builtins: ("range_check_builtin": 32) - syscalls: (StorageWrite: 1, StorageRead: 1, CallContract: 1) -... +Collected 2 test(s) from hello_starknet package +Running 2 test(s) from tests/ +[PASS] hello_starknet_integrationtest::test_contract::test_cannot_increase_balance_with_zero_value (gas: ~105) + steps: 3405 + memory holes: 22 + builtins: (range_check: 77, pedersen: 7) + syscalls: (CallContract: 2, StorageRead: 1, Deploy: 1) + +[PASS] hello_starknet_integrationtest::test_contract::test_increase_balance (gas: ~172) + steps: 4535 + memory holes: 15 + builtins: (range_check: 95, pedersen: 7) + syscalls: (CallContract: 3, StorageRead: 3, Deploy: 1, StorageWrite: 1) + +Running 0 test(s) from src/ +Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ```
diff --git a/docs/src/testing/running-tests.md b/docs/src/testing/running-tests.md index bf3382e80b..63f4aa7419 100644 --- a/docs/src/testing/running-tests.md +++ b/docs/src/testing/running-tests.md @@ -10,11 +10,12 @@ $ snforge test Output: ```shell -Collected 3 test(s) from package_name package -Running 3 test(s) from src/ -[PASS] package_name::tests::executing -[PASS] package_name::tests::calling -[PASS] package_name::tests::calling_another +Collected 3 test(s) from hello_snforge package +Running 0 test(s) from src/ +Running 3 test(s) from tests/ +[PASS] hello_snforge_integrationtest::test_contract::test_calling (gas: ~1) +[PASS] hello_snforge_integrationtest::test_contract::test_executing (gas: ~1) +[PASS] hello_snforge_integrationtest::test_contract::test_calling_another (gas: ~1) Tests: 3 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ``` @@ -23,8 +24,7 @@ Tests: 3 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ## Filtering Tests You can pass a filter string after the `snforge test` command to filter tests. -By default, any test with an [absolute module tree path](https://book.cairo-lang.org/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#paths-for-referring-to-an-item-in-the-module-tree) - matching the filter will be run. +By default, any test with an [absolute module tree path](https://book.cairo-lang.org/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#paths-for-referring-to-an-item-in-the-module-tree) matching the filter will be run. ```shell $ snforge test calling @@ -34,10 +34,11 @@ $ snforge test calling Output: ```shell -Collected 2 test(s) from package_name package -Running 2 test(s) from src/ -[PASS] package_name::tests::calling -[PASS] package_name::tests::calling_another +Collected 2 test(s) from hello_snforge package +Running 0 test(s) from src/ +Running 2 test(s) from tests/ +[PASS] hello_snforge_integrationtest::test_contract::test_calling_another (gas: ~1) +[PASS] hello_snforge_integrationtest::test_contract::test_calling (gas: ~1) Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 1 filtered out ``` @@ -54,16 +55,17 @@ Note, you have to use a fully qualified test name, including a module name. > ```shell -$ snforge test package_name::tests::calling --exact +$ snforge test hello_snforge_integrationtest::test_contract::test_calling --exact ```
Output: ```shell -Collected 1 test(s) from package_name package -Running 1 test(s) from src/ -[PASS] package_name::tests::calling +Collected 1 test(s) from hello_snforge package +Running 1 test(s) from tests/ +[PASS] hello_snforge_integrationtest::test_contract::test_calling (gas: ~1) +Running 0 test(s) from src/ Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, other filtered out ```
@@ -73,6 +75,7 @@ Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, other filtered out To stop the test execution after first failed test, you can pass an `--exit-first` flag along with `snforge test` command. + ```shell $ snforge test --exit-first ``` @@ -81,20 +84,18 @@ $ snforge test --exit-first Output: ```shell -Collected 6 test(s) from package_name package -Running 6 test(s) from src/ -[PASS] package_name::tests::executing -[PASS] package_name::tests::calling -[PASS] package_name::tests::calling_another -[FAIL] package_name::tests::failing +Collected 3 test(s) from failing_example package +Running 3 test(s) from tests/ +[FAIL] failing_example_tests::test_failing Failure data: 0x6661696c696e6720636865636b ('failing check') -Tests: 3 passed, 1 failed, 2 skipped, 0 ignored, 0 filtered out Failures: - package_name::tests::failing + failing_example_tests::test_failing + +Tests: 0 passed, 1 failed, 2 skipped, 0 ignored, 0 filtered out ```
@@ -111,15 +112,22 @@ $ snforge test --detailed-resources Output: ```shell -Collected 1 test(s) from package_name package -Running 1 test(s) from src/ -[PASS] package_name::tests::resources (gas: ~2213) - steps: 881 - memory holes: 36 - builtins: ("range_check_builtin": 32) - syscalls: (StorageWrite: 1, StorageRead: 1, CallContract: 1) - -Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out +Collected 2 test(s) from hello_starknet package +Running 2 test(s) from tests/ +[PASS] hello_starknet_integrationtest::test_contract::test_cannot_increase_balance_with_zero_value (gas: ~105) + steps: 3405 + memory holes: 22 + builtins: (range_check: 77, pedersen: 7) + syscalls: (CallContract: 2, StorageRead: 1, Deploy: 1) + +[PASS] hello_starknet_integrationtest::test_contract::test_increase_balance (gas: ~172) + steps: 4535 + memory holes: 15 + builtins: (range_check: 95, pedersen: 7) + syscalls: (CallContract: 3, StorageRead: 3, Deploy: 1, StorageWrite: 1) + +Running 0 test(s) from src/ +Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ```
diff --git a/docs/src/testing/test-collection.md b/docs/src/testing/test-collection.md index c8535dfa17..f084eb1069 100644 --- a/docs/src/testing/test-collection.md +++ b/docs/src/testing/test-collection.md @@ -1,111 +1,127 @@ -## Test Collection +# How Tests Are Collected -`snforge` considers all functions in your project marked with `#[test]` attribute as tests. -By default, test functions run without any arguments. -However, adding any arguments to function signature will enable [fuzz testing](../snforge-advanced-features/fuzz-testing.md) for this -test case. +Snforge executes tests, but it does not compile them directly. +Instead, it compiles tests by internally running `scarb build --test` command. -`snforge` will collect tests only from these places: +The `snforge_scarb_plugin` dependency, which is included with `snforge_std` dependency makes all functions +marked with `#[test]` executable and indicates to Scarb they should be compiled. +Without the plugin, no snforge tests can be compiled, that's why `snforge_std` dependency is always required in all +snforge projects. -- any files reachable from the package root (declared as `mod` in `lib.cairo` or its children) - -these have to be in a module annotated with `#[cfg(test)]` -- files inside the [`tests`](#the-tests-directory) directory +Thanks to that, Scarb collects all functions marked with `#[test]` +from [valid locations](https://docs.swmansion.com/scarb/docs/extensions/testing.html#tests-organization) and compiles +them into tests that are executed by snforge. -## The `tests` Directory +## `[[test]]` Target -`snforge` collects tests from `tests` directory. -Depending on the presence of `tests/lib.cairo` file, the behavior of the test collector will be different. +Under the hood, Scarb utilizes the `[[test]]` target mechanism to compile the tests. More information about the +`[[test]]` target is available in +the [Scarb documentation](https://docs.swmansion.com/scarb/docs/reference/targets.html#test-targets). -### With `tests/lib.cairo` +By default, `[[test]]]` target is implicitly configured and user does not have to define it. +See [Scarb documentation](https://docs.swmansion.com/scarb/docs/reference/targets.html#auto-detection-of-test-targets) +for more details about the mechanism. -If there is a `lib.cairo` file in `tests` folder, -then it is treated as an entrypoint to the `tests` package from which tests are collected. +## Tests Organization -For example, for a package structured this way: +Test can be placed in both `src` and `test` directories. When adding tests to files in `src` you must wrap them in tests +module. -```shell -$ tree . -``` +You can read more about tests organization +in [Scarb documentation](https://docs.swmansion.com/scarb/docs/extensions/testing.html#tests-organization). -
-Output: +### Unit Tests -```shell -. -├── Scarb.toml -├── tests/ -│ ├── lib.cairo -│ ├── common/ -│ │ └── utils.cairo -│ ├── common.cairo -│ ├── test_contract.cairo -│ └── not_included.cairo -└── src/ - └── lib.cairo -``` -
-
- -with `tests/lib.cairo` content: +Test placed in `src` directory are often called unit tests. +For these test to function in snforge, they must be wrapped in a module marked with `#[cfg(test)]` attribute. ```rust -mod common; -mod test_contract; +// src/example.rs +// ... + +// This test is not in module marked with `#[cfg(test)]` so it won't work +#[test] +fn my_invalid_test() { + // ... +} + +#[cfg(test)] +mod tests { + // This test is in module marked with `#[cfg(test)]` so it will work + #[test] + fn my_test() { + // .. + } +} ``` -and `tests/common.cairo` content: +### Integration Tests + +Integration tests are placed in `tests` directory. +This directory is a special directory in Scarb. +Tests do not have to be wrapped in `#[cfg(test)]` and each file is treated as a separate module. ```rust -mod utils; +// tests/example.rs +// ... + +// This test is in `tests` directory +// so it works without being in module with `#[cfg(test)]` +#[test] +fn my_test_1() { + // .. +} ``` -tests from `tests/lib.cairo`, `tests/test_contract.cairo`, `tests/common.cairo` -and `tests/common/utils.cairo` will be collected. - -### Without `tests/lib.cairo` +#### Modules and `lib.cairo` -When there is no `lib.cairo` present in `tests` folder, -all test files **directly** in `tests` directory (i.e., not in its subdirectories) -are treated as modules and added to a single virtual `lib.cairo`. -Then this virtual `lib.cairo` is treated as an entrypoint to the `tests` package from which tests are collected. - -For example, for a package structured this way: +As written above, each file in `tests` directory is treated as a separate module ```shell -$ tree . +$ tree ``` -
+
Output: ```shell -. -├── Scarb.toml -├── tests/ -│ ├── common/ -│ │ └── utils.cairo -│ ├── common.cairo -│ ├── test_contract.cairo -│ └── not_included/ -│ └── ignored.cairo -└── src/ - └── lib.cairo +tests/ +├── module1.cairo <-- is collected +├── module2.cairo <-- is collected +└── module3.cairo <-- is collected ``` +
-
-and `tests/common.cairo` content: +Scarb will collect each file and compile it as a +separate [test target](https://docs.swmansion.com/scarb/docs/reference/targets.html#test-targets). +Each of these targets will be run separately by `snforge`. -```rust -mod utils; +However, it is also possible to define `lib.cairo` file in `tests`. +This stops files in `tests` from being treated as separate modules. +Instead, Scarb will only create a single test target for that `lib.cairo` file. +Only tests that are reachable from this file will be collected and compiled. + +```shell +$ tree ``` -tests from `tests/test_contract.cairo`, `tests/common.cairo` and `tests/common/utils.cairo` will be collected. +
+Output: -### Sharing Code Between Tests +```shell +tests/ +├── lib.cairo +├── module1.cairo <-- is collected +├── module2.cairo <-- is collected +└── module3.cairo <-- is not collected +``` -Sometimes you may want a share some code between tests to organize them. -The package structure of tests makes it easy! -In both of the above examples, you can -make the functions from `tests/common/utils.cairo` available in `tests/test_contract.cairo` -by using a relative import: `use super::common::utils;`. +
+ +```rust +// tests/lib.cairo + +mod module1; +mod module2; +``` diff --git a/docs/src/testing/testing-contract-internals.md b/docs/src/testing/testing-contract-internals.md index 8bf3aff494..f653f49cf2 100644 --- a/docs/src/testing/testing-contract-internals.md +++ b/docs/src/testing/testing-contract-internals.md @@ -18,7 +18,7 @@ This is a function generated by the `#[starknet::contract]` macro. It can be used to test some functions which accept the state as an argument, see the example below: ```rust -{{#include ../../listings/snforge_overview/crates/testing_contract_internals/src/basic_example.cairo}} +{{#include ../../listings/testing_contract_internals/src/basic_example.cairo}} ``` This code contains some caveats: @@ -38,7 +38,7 @@ Example usages: Example for `cheat_block_number`, same can be implemented for `cheat_caller_address`/`cheat_block_timestamp`/`elect` etc. ```rust -{{#include ../../listings/snforge_overview/crates/testing_contract_internals/tests/mocking_the_context_info.cairo}} +{{#include ../../listings/testing_contract_internals/tests/mocking_the_context_info.cairo}} ``` #### 2. Spying for events You can use both `starknet::emit_event_syscall`, and the spies will capture the events, @@ -47,17 +47,17 @@ emitted in a `#[test]` function, if you pass the `test_address()` as a spy param Given the emitting contract implementation: ```rust -{{#include ../../listings/snforge_overview/crates/testing_contract_internals/src/spying_for_events.cairo}} +{{#include ../../listings/testing_contract_internals/src/spying_for_events.cairo}} ``` You can implement this test: ```rust -{{#include ../../listings/snforge_overview/crates/testing_contract_internals/tests/spying_for_events/tests.cairo}} +{{#include ../../listings/testing_contract_internals/tests/spying_for_events/tests.cairo}} ``` You can also use the `starknet::emit_event_syscall` directly in the tests: ```rust -{{#include ../../listings/snforge_overview/crates/testing_contract_internals/tests/spying_for_events/syscall_tests.cairo}} +{{#include ../../listings/testing_contract_internals/tests/spying_for_events/syscall_tests.cairo}} ``` ## Using Library Calls With the Test State Context @@ -67,11 +67,11 @@ Using the above utilities, you can avoid deploying a mock contract, to test a `l For contract implementation: ```rust -{{#include ../../listings/snforge_overview/crates/testing_contract_internals/src/using_library_calls.cairo}} +{{#include ../../listings/testing_contract_internals/src/using_library_calls.cairo}} ``` We use the `SafeLibraryDispatcher` like this: ```rust -{{#include ../../listings/snforge_overview/crates/testing_contract_internals/tests/using_library_calls.cairo}} +{{#include ../../listings/testing_contract_internals/tests/using_library_calls.cairo}} ``` > ⚠️ **Warning** > diff --git a/docs/src/testing/testing-events.md b/docs/src/testing/testing-events.md index 31342fc6ca..37cf58dbcd 100644 --- a/docs/src/testing/testing-events.md +++ b/docs/src/testing/testing-events.md @@ -2,7 +2,7 @@ Examples are based on the following `SpyEventsChecker` contract implementation: ```rust -{{#include ../../listings/snforge_overview/crates/testing_events/src/contract.cairo}} +{{#include ../../listings/testing_events/src/contract.cairo}} ``` ## Asserting emission with `assert_emitted` method @@ -11,7 +11,7 @@ This is the simpler way, in which you don't have to fetch the events explicitly. See the below code for reference: ```rust -{{#include ../../listings/snforge_overview/crates/testing_events/tests/assert_emitted.cairo}} +{{#include ../../listings/testing_events/tests/assert_emitted.cairo}} ``` Let's go through the code: @@ -52,7 +52,7 @@ Simply call `get_events()` on your `EventSpy` and access `events` field on the Then, you can access the events and assert data by yourself. ```rust -{{#include ../../listings/snforge_overview/crates/testing_events/tests/assert_manually.cairo}} +{{#include ../../listings/testing_events/tests/assert_manually.cairo}} ``` Let's go through important parts of the provided code: @@ -75,7 +75,7 @@ Sometimes, when you assert the events manually, you might not want to get all th a particular address. You can address that by using the method `emitted_by` on the `Events` structure. ```rust -{{#include ../../listings/snforge_overview/crates/testing_events/tests/filter.cairo}} +{{#include ../../listings/testing_events/tests/filter.cairo}} ``` `events_from_first_address` has events emitted by the first contract only. @@ -89,13 +89,13 @@ They can also be asserted with `spy.assert_emitted` method. Let's extend our `SpyEventsChecker` with `emit_event_with_syscall` method: ```rust -{{#include ../../listings/snforge_overview/crates/testing_events/src/syscall_dummy.cairo}} +{{#include ../../listings/testing_events/src/syscall_dummy.cairo}} ``` And add a test for it: ```rust -{{#include ../../listings/snforge_overview/crates/testing_events/tests/syscall.cairo}} +{{#include ../../listings/testing_events/tests/syscall.cairo}} ``` Using `Event` struct from the `snforge_std` library we can easily assert nonstandard events. diff --git a/docs/src/testing/testing-messages-to-l1.md b/docs/src/testing/testing-messages-to-l1.md index fe0c490bbb..74902deafb 100644 --- a/docs/src/testing/testing-messages-to-l1.md +++ b/docs/src/testing/testing-messages-to-l1.md @@ -34,11 +34,11 @@ With the spy ready to use, you can execute some code, and make the assertions: 1. Either with the spy directly by using `assert_sent`/`assert_not_sent` methods from `MessageToL1SpyAssertionsTrait` trait: ```rust -{{#include ../../listings/snforge_overview/crates/testing_messages_to_l1/tests/simple.cairo}} +{{#include ../../listings/testing_messages_to_l1/tests/simple.cairo}} ``` 2. Or use the messages' contents directly via `get_messages()` method of the `MessageToL1SpyTrait`: ```rust -{{#include ../../listings/snforge_overview/crates/testing_messages_to_l1/tests/detailed.cairo}} +{{#include ../../listings/testing_messages_to_l1/tests/detailed.cairo}} ``` diff --git a/docs/src/testing/testing-workspaces.md b/docs/src/testing/testing-workspaces.md index a959874cf4..63eefb8459 100644 --- a/docs/src/testing/testing-workspaces.md +++ b/docs/src/testing/testing-workspaces.md @@ -46,10 +46,25 @@ $ snforge test Output: ```shell -Collected 1 test(s) from hello_workspaces package +Collected 3 test(s) from hello_workspaces package Running 1 test(s) from src/ -[PASS] hello_workspaces::tests::test_simple -Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out +[PASS] hello_workspaces::tests::test_simple (gas: ~1) +Running 2 test(s) from tests/ +[FAIL] hello_workspaces_integrationtest::test_failing::test_failing + +Failure data: + 0x6661696c696e6720636865636b ('failing check') + +[FAIL] hello_workspaces_integrationtest::test_failing::test_another_failing + +Failure data: + 0x6661696c696e6720636865636b ('failing check') + +Tests: 1 passed, 2 failed, 0 skipped, 0 ignored, 0 filtered out + +Failures: + hello_workspaces_integrationtest::test_failing::test_failing + hello_workspaces_integrationtest::test_failing::test_another_failing ```

@@ -57,7 +72,7 @@ Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out To select the specific package to test, pass a `--package package_name` (or `-p package_name` for short) flag. You can also run `snforge test` from the package directory to achieve the same effect. - + ```shell $ snforge test --package addition ``` @@ -66,12 +81,15 @@ $ snforge test --package addition Output: ```shell -Collected 2 test(s) from addition package +Collected 5 test(s) from addition package +Running 4 test(s) from tests/ +[PASS] addition_integrationtest::nested::test_nested::test_two (gas: ~1) +[PASS] addition_integrationtest::nested::test_nested::test_two_and_two (gas: ~1) +[PASS] addition_integrationtest::nested::simple_case (gas: ~1) +[PASS] addition_integrationtest::nested::contract_test (gas: ~1) Running 1 test(s) from src/ -[PASS] addition::tests::it_works -Running 1 test(s) from tests/ -[PASS] tests::test_simple::simple_case -Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out +[PASS] addition::tests::it_works (gas: ~1) +Tests: 5 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ```
@@ -86,24 +104,53 @@ $ snforge test --workspace Output: ```shell -Collected 2 test(s) from addition package +Collected 5 test(s) from addition package +Running 4 test(s) from tests/ +[PASS] addition_integrationtest::nested::test_nested::test_two (gas: ~1) +[PASS] addition_integrationtest::nested::simple_case (gas: ~1) +[PASS] addition_integrationtest::nested::test_nested::test_two_and_two (gas: ~1) +[PASS] addition_integrationtest::nested::contract_test (gas: ~1) Running 1 test(s) from src/ -[PASS] addition::tests::it_works -Running 1 test(s) from tests/ -[PASS] tests::test_simple::simple_case -Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out +[PASS] addition::tests::it_works (gas: ~1) +Tests: 5 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out -Collected 1 test(s) from fibonacci package -Running 1 test(s) from src/ -[PASS] fibonacci::tests::it_works -Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out +Collected 6 test(s) from fibonacci package +Running 2 test(s) from src/ +[PASS] fibonacci::tests::it_works (gas: ~1) +[PASS] fibonacci::tests::contract_test (gas: ~1) +Running 4 test(s) from tests/ +[FAIL] fibonacci_tests::abc::efg::failing_test + +Failure data: + 0x0 ('') +[PASS] fibonacci_tests::abc::efg::efg_test (gas: ~1) +[PASS] fibonacci_tests::lib_test (gas: ~1) +[PASS] fibonacci_tests::abc::abc_test (gas: ~1) +Tests: 5 passed, 1 failed, 0 skipped, 0 ignored, 0 filtered out -Collected 1 test(s) from hello_workspaces package + +Collected 3 test(s) from hello_workspaces package Running 1 test(s) from src/ -[PASS] hello_workspaces::tests::test_simple -Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out +[PASS] hello_workspaces::tests::test_simple (gas: ~1) +Running 2 test(s) from tests/ +[FAIL] hello_workspaces_integrationtest::test_failing::test_another_failing + +Failure data: + 0x6661696c696e6720636865636b ('failing check') + +[FAIL] hello_workspaces_integrationtest::test_failing::test_failing + +Failure data: + 0x6661696c696e6720636865636b ('failing check') + +Tests: 1 passed, 2 failed, 0 skipped, 0 ignored, 0 filtered out + +Failures: + fibonacci_tests::abc::efg::failing_test + hello_workspaces_integrationtest::test_failing::test_another_failing + hello_workspaces_integrationtest::test_failing::test_failing ```
diff --git a/docs/src/testing/testing.md b/docs/src/testing/testing.md index bdff23bcc6..229fdb718d 100644 --- a/docs/src/testing/testing.md +++ b/docs/src/testing/testing.md @@ -8,7 +8,7 @@ should write as many unit tests as possible as these are faster than integration First, add the following code to the `src/lib.cairo` file: ```rust -{{#include ../../listings/snforge_overview/crates/writing_tests/src/first_test.cairo}} +{{#include ../../listings/first_test/src/lib.cairo}} ``` It is a common practice to keep your unit tests in the same file as the tested code. @@ -26,9 +26,9 @@ $ snforge test Output: ```shell -Collected 1 test(s) from writing_tests package +Collected 1 test(s) from first_test package Running 1 test(s) from src/ -[PASS] writing::first_test::tests::test_sum +[PASS] first_test::tests::test_sum (gas: ~1) Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ``` @@ -39,8 +39,7 @@ Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out If your code panics, the test is considered failed. Here's an example of a failing test. ```rust -{{#include ../../listings/snforge_overview/crates/writing_tests/src/panicking_tests.cairo:first_half}} -{{#include ../../listings/snforge_overview/crates/writing_tests/src/panicking_tests.cairo:second_half}} +{{#include ../../listings/panicking_test/src/lib.cairo}} ``` ```shell @@ -51,21 +50,23 @@ $ snforge test Output: ```shell -Collected 1 test(s) from writing_tests package +Collected 1 test(s) from panicking_test package Running 1 test(s) from src/ -[FAIL] writing_tests::panicking_tests::tests::failing +[FAIL] panicking_test::tests::failing Failure data: - 0x616161 ('aaa') + 0x70616e6963206d657373616765 ('panic message') Tests: 0 passed, 1 failed, 0 skipped, 0 ignored, 0 filtered out Failures: - writing_tests::panicking_tests::tests::failing + panicking_test::tests::failing ```
+When contract fails, you can get backtrace information by setting the `SNFORGE_BACKTRACE=1` environment variable. Read more about it [here](../snforge-advanced-features/backtrace.md). + ## Expected Failures Sometimes you want to mark a test as expected to fail. This is useful when you want to verify that an action fails as @@ -77,18 +78,18 @@ You can specify the expected failure message in three ways: 1. **With ByteArray**: ```rust -{{#include ../../listings/snforge_overview/crates/writing_tests/tests/expected_failures.cairo:byte_array}} +{{#include ../../listings/should_panic_example/src/lib.cairo:byte_array}} ``` With this format, the expected error message needs to be a substring of the actual error message. This is particularly useful when the error message includes dynamic data such as a hash or address. 2. **With felt** ```rust -{{#include ../../listings/snforge_overview/crates/writing_tests/tests/expected_failures.cairo:felt}} +{{#include ../../listings/should_panic_example/src/lib.cairo:felt}} ``` 3. **With tuple of felts**: ```rust -{{#include ../../listings/snforge_overview/crates/writing_tests/tests/expected_failures.cairo:tuple}} +{{#include ../../listings/should_panic_example/src/lib.cairo:tuple}} ``` @@ -100,11 +101,14 @@ $ snforge test Output: ```shell -Collected 1 test(s) from writing_tests package -Running 0 test(s) from src/ -Running 1 test(s) from tests/ -[PASS] snforge_overview_integrationtest::should_panic_check_data -Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out +Collected 5 test(s) from should_panic_example package +Running 5 test(s) from src/ +[PASS] should_panic_example::tests::should_panic_felt_matching (gas: ~1) +[PASS] should_panic_example::tests::should_panic_multiple_messages (gas: ~1) +[PASS] should_panic_example::tests::should_panic_exact (gas: ~1) +[PASS] should_panic_example::tests::should_panic_expected_is_substring (gas: ~1) +[PASS] should_panic_example::tests::should_panic_check_data (gas: ~1) +Tests: 5 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ```
@@ -115,7 +119,7 @@ Sometimes you may have tests that you want to exclude during most runs of `snfor You can achieve it using `#[ignore]` - tests marked with this attribute will be skipped by default. ```rust -{{#include ../../listings/snforge_overview/crates/writing_tests/tests/ignoring.cairo}} +{{#include ../../listings/ignoring_example/src/lib.cairo}} ``` ```shell @@ -126,10 +130,9 @@ $ snforge test Output: ```shell -Collected 1 test(s) from writing_tests package -Running 0 test(s) from src/ -Running 1 test(s) from tests/ -[IGNORE] writing_tests_integrationtest::ignoring::ignored_test +Collected 1 test(s) from ignoring_example package +Running 1 test(s) from src/ +[IGNORE] ignoring_example::tests::ignored_test Tests: 0 passed, 0 failed, 0 skipped, 1 ignored, 0 filtered out ``` @@ -141,11 +144,11 @@ To run all tests regardless of the `#[ignore]` attribute use `snforge test --inc ## Writing Assertions and `assert_macros` Package > ⚠️ **Recommended only for development** ️⚠️ > ->***Assert macros package provides a set of macros that can be used to write assertions such as `assert_eq!`. +> Assert macros package provides a set of macros that can be used to write assertions such as `assert_eq!`. In order to use it, your project must have the `assert_macros` dependency added to the `Scarb.toml` file. These macros are very expensive to run on Starknet, as they result a huge amount of steps and are not recommended for production use. They are only meant to be used in tests. -For snforge `v0.31.0` and later, this dependency is added automatically when creating a project using `snforge init`. But for earlier versions, you need to add it manually.*** +For snforge `v0.31.0` and later, this dependency is added automatically when creating a project using `snforge init`. But for earlier versions, you need to add it manually. ```toml [dev-dependencies] diff --git a/docs/src/testing/using-cheatcodes.md b/docs/src/testing/using-cheatcodes.md index eb8d0e6202..1ce48ad934 100644 --- a/docs/src/testing/using-cheatcodes.md +++ b/docs/src/testing/using-cheatcodes.md @@ -23,7 +23,7 @@ using [cheatcodes](../appendix/cheatcodes.md). In this tutorial, we will be using the following Starknet contract: ```rust -{{#include ../../listings/snforge_overview/crates/using_cheatcodes/src/lib.cairo}} +{{#include ../../listings/using_cheatcodes/src/lib.cairo}} ``` ## Writing Tests @@ -31,8 +31,7 @@ In this tutorial, we will be using the following Starknet contract: We can try to create a test that will increase and verify the balance. ```rust -{{#include ../../listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/failing.cairo:first_half}} -{{#include ../../listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/failing.cairo:second_half}} +{{#include ../../listings/using_cheatcodes/tests/lib.cairo}} ``` This test fails, which means that `increase_balance` method panics as we expected. @@ -46,13 +45,17 @@ $ snforge test ```shell Collected 1 test(s) from using_cheatcodes package +Running 0 test(s) from src/ Running 1 test(s) from tests/ -[FAIL] using_cheatcodes_tests::caller_address::failing::call_and_invoke +[FAIL] using_cheatcodes_tests::call_and_invoke Failure data: 0x75736572206973206e6f7420616c6c6f776564 ('user is not allowed') Tests: 0 passed, 1 failed, 0 skipped, 0 ignored, 0 filtered out + +Failures: + using_cheatcodes_tests::call_and_invoke ```
@@ -68,7 +71,7 @@ address, so it passes our validation. ### Cheating an Address ```rust -{{#include ../../listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/proper_use.cairo}} +{{#include ../../listings/using_cheatcodes_cheat_address/tests/lib.cairo}} ``` The test will now pass without an error @@ -81,16 +84,16 @@ $ snforge test Output: ```shell -Collected 1 test(s) from using_cheatcodes package +Collected 1 test(s) from using_cheatcodes_cheat_address package Running 0 test(s) from src/ Running 1 test(s) from tests/ -[PASS] using_cheatcodes_integrationtest::caller_address::proper_use::call_and_invoke (gas: ~239) +[PASS] using_cheatcodes_cheat_address_tests::call_and_invoke (gas: ~239) Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ```
-### Canceling the Cheat +### Cancelling the Cheat Most cheatcodes come with corresponding `start_` and `stop_` functions that can be used to start and stop the state change. @@ -99,8 +102,7 @@ using [`stop_cheat_caller_address`](../appendix/cheatcodes/caller_address.md#sto We will demonstrate its behavior using `SafeDispatcher` to show when exactly the fail occurs: ```rust -{{#include ../../listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/cancel.cairo:first_half}} -{{#include ../../listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/cancel.cairo:second_half}} +{{#include ../../listings/using_cheatcodes_cancelling_cheat/tests/lib.cairo}} ``` ```shell @@ -111,15 +113,18 @@ $ snforge test Output: ```shell -Collected 1 test(s) from using_cheatcodes package -Running 0 test(s) from src/ +Collected 1 test(s) from using_cheatcodes_cancelling_cheat package Running 1 test(s) from tests/ -[FAIL] using_cheatcodes_tests::caller_address::cancel::call_and_invoke +[FAIL] using_cheatcodes_cancelling_cheat_tests::call_and_invoke Failure data: 0x5365636f6e642063616c6c206661696c656421 ('Second call failed!') -Tests: 0 passed, 1 failed, 0 skipped, 0 ignored, 4 filtered out +Running 0 test(s) from src/ +Tests: 0 passed, 1 failed, 0 skipped, 0 ignored, 0 filtered out + +Failures: + using_cheatcodes_cancelling_cheat_tests::call_and_invoke ```
@@ -132,7 +137,7 @@ In case you want to cheat the caller address for all contracts, you can use the For more see [Cheating Globally](../appendix/cheatcodes/global.md). ```rust -{{#include ../../listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/proper_use_global.cairo}} +{{#include ../../listings/using_cheatcodes_others/tests/caller_address/proper_use_global.cairo}} ``` ### Cheating the Constructor @@ -144,7 +149,7 @@ Let's say, that you have a contract that saves the caller address (deployer) in To `cheat_caller_address` the constructor, you need to `start_cheat_caller_address` before it is invoked, with the right address. To achieve this, you need to precalculate the address of the contract by using the `precalculate_address` function of `ContractClassTrait` on the declared contract, and then use it in `start_cheat_caller_address` as an argument: ```rust -{{#include ../../listings/snforge_overview/crates/using_cheatcodes/tests/cheat_constructor.cairo}} +{{#include ../../listings/using_cheatcodes_others/tests/cheat_constructor.cairo}} ``` ### Setting Cheatcode Span @@ -178,5 +183,5 @@ Of course the cheatcode can still be canceled before its `CheatSpan` goes down t To better understand the functionality of `CheatSpan`, here's a full example: ```rust -{{#include ../../listings/snforge_overview/crates/using_cheatcodes/tests/caller_address/span.cairo}} +{{#include ../../listings/using_cheatcodes_others/tests/caller_address/span.cairo}} ``` diff --git a/scripts/get_scarb_versions.sh b/scripts/get_scarb_versions.sh new file mode 100755 index 0000000000..887d479236 --- /dev/null +++ b/scripts/get_scarb_versions.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -e + +# This script is used to find the following Scarb versions: +# The current major.minor version along with all its patch versions +# The latest patch versions for the two versions preceding the current one + +function get_all_patch_versions() { + # Omit version 2.8.0 as it has a bug when using the `assert_macros` package + asdf list all scarb "$1" | grep -v "rc" | grep -v "2.8.0" +} + +function get_latest_patch_version() { + get_all_patch_versions "$1" | sort -uV | tail -1 +} + +major_minor_versions=($(get_all_patch_versions | cut -d . -f 1,2 | sort -uV | tail -3)) + +declare -a scarb_versions + +if [[ ${major_minor_versions[0]} != "2.6" ]]; then + scarb_versions+=($(get_latest_patch_version "${major_minor_versions[0]}")) +fi + +scarb_versions+=($(get_latest_patch_version "${major_minor_versions[1]}")) + +scarb_versions+=($(get_all_patch_versions "${major_minor_versions[2]}")) + +printf '"%s", ' "${scarb_versions[@]}" | sed 's/, $/\n/' diff --git a/scripts/set_plugin_version.sh b/scripts/set_plugin_version.sh new file mode 100755 index 0000000000..2e397f57f5 --- /dev/null +++ b/scripts/set_plugin_version.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +PLUGIN_FILE_PATH="../crates/snforge-scarb-plugin/Scarb.toml" +SNFORGE_STD_PATH="../snforge_std/Scarb.toml" + +VERSION=$(grep version "$PLUGIN_FILE_PATH" | cut -d '"' -f 2) + +sed -i.bak "/snforge_scarb_plugin/ s/\(snforge_scarb_plugin = \).*/\1\"^${VERSION}\"/" $SNFORGE_STD_PATH + +rm ${SNFORGE_STD_PATH}.bak 2> /dev/null diff --git a/scripts/verify_cairo_listings.sh b/scripts/verify_cairo_listings.sh index 6f5f4245c3..a49c86a437 100755 --- a/scripts/verify_cairo_listings.sh +++ b/scripts/verify_cairo_listings.sh @@ -1,4 +1,5 @@ #!/bin/bash -set -e +set -xe -for d in ./docs/listings/*; do (cd "$d" && scarb test); done +# TODO(#2718) +for d in ./docs/listings/*; do (cd "$d" && scarb build); done diff --git a/sncast_std/Scarb.lock b/sncast_std/Scarb.lock index 8f3a361385..0cf0672dbc 100644 --- a/sncast_std/Scarb.lock +++ b/sncast_std/Scarb.lock @@ -3,4 +3,4 @@ version = 1 [[package]] name = "sncast_std" -version = "0.33.0" +version = "0.34.0" diff --git a/sncast_std/Scarb.toml b/sncast_std/Scarb.toml index 2a5f3baf24..c78e7e2b71 100644 --- a/sncast_std/Scarb.toml +++ b/sncast_std/Scarb.toml @@ -1,6 +1,6 @@ [package] name = "sncast_std" -version = "0.33.0" +version = "0.34.0" edition = "2023_11" description = "Library used for writing deployment scripts in Cairo" homepage = "https://foundry-rs.github.io/starknet-foundry/starknet/script.html" diff --git a/snforge_std/Scarb.lock b/snforge_std/Scarb.lock index d7f0c48a44..1d45c2d1b6 100644 --- a/snforge_std/Scarb.lock +++ b/snforge_std/Scarb.lock @@ -3,11 +3,11 @@ version = 1 [[package]] name = "snforge_scarb_plugin" -version = "0.33.0" +version = "0.34.0" [[package]] name = "snforge_std" -version = "0.33.0" +version = "0.34.0" dependencies = [ "snforge_scarb_plugin", ] diff --git a/snforge_std/Scarb.toml b/snforge_std/Scarb.toml index bf2f21a52e..188602ef5d 100644 --- a/snforge_std/Scarb.toml +++ b/snforge_std/Scarb.toml @@ -1,6 +1,6 @@ [package] name = "snforge_std" -version = "0.33.0" +version = "0.34.0" edition = "2023_10" description = "Cairo testing library" documentation = "https://foundry-rs.github.io/starknet-foundry/appendix/snforge-library.html"