diff --git a/official/docs/csharp/current/trackers/batch.cs b/official/docs/csharp/current/trackers/batch.cs new file mode 100644 index 00000000..7201f237 --- /dev/null +++ b/official/docs/csharp/current/trackers/batch.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using EasyPost; +using Newtonsoft.Json; + +namespace EasyPostExamples +{ + public class Examples + { + public static async Task Main() + { + var client = new EasyPost.Client(new EasyPost.ClientConfiguration("EASYPOST_API_KEY")); + + EasyPost.Parameters.Tracker.Batch parameters = new() + { + TrackingCodes = new List { "EZ1000000001", "EZ1000000002" } + }; + + EasyPost.Models.API.TrackerCollection trackerCollection = await client.Tracker.RetrieveBatch(parameters); + + Console.WriteLine(JsonConvert.SerializeObject(trackerCollection, Formatting.Indented)); + } + } +} diff --git a/official/docs/curl/current/trackers/batch.sh b/official/docs/curl/current/trackers/batch.sh new file mode 100644 index 00000000..74b9dd37 --- /dev/null +++ b/official/docs/curl/current/trackers/batch.sh @@ -0,0 +1,9 @@ +curl -X POST https://api.easypost.com/v2/trackers/batch \ + -u "EASYPOST_API_KEY": \ + -H 'Content-Type: application/json' \ + -d '{ + "tracking_codes": [ + "EZ1000000001", + "EZ1000000002" + ] + }' diff --git a/official/docs/golang/current/trackers/batch.go b/official/docs/golang/current/trackers/batch.go new file mode 100644 index 00000000..c5e44a3c --- /dev/null +++ b/official/docs/golang/current/trackers/batch.go @@ -0,0 +1,19 @@ +package example + +import ( + "fmt" + + "github.com/EasyPost/easypost-go/v5" +) + +func batch() { + client := easypost.New("EASYPOST_API_KEY") + + trackers, _ := client.RetrieveTrackerBatch( + &easypost.ListTrackersOptions{ + TrackingCodes: []string{"EZ1000000001", "EZ1000000002"}, + }, + ) + + fmt.Println(trackers) +} diff --git a/official/docs/java/current/trackers/batch.java b/official/docs/java/current/trackers/batch.java new file mode 100644 index 00000000..0fa8712f --- /dev/null +++ b/official/docs/java/current/trackers/batch.java @@ -0,0 +1,21 @@ +package trackers; + +import java.util.Arrays; +import java.util.HashMap; + +import com.easypost.exception.EasyPostException; +import com.easypost.model.TrackerCollection; +import com.easypost.service.EasyPostClient; + +public class Batch { + public static void main(String[] args) throws EasyPostException { + EasyPostClient client = new EasyPostClient("EASYPOST_API_KEY"); + + HashMap params = new HashMap(); + params.put("tracking_codes", Arrays.asList("EZ1000000001", "EZ1000000002")); + + TrackerCollection trackers = client.tracker.retrieveBatch(params); + + System.out.println(trackers); + } +} diff --git a/official/docs/node/current/trackers/batch.js b/official/docs/node/current/trackers/batch.js new file mode 100644 index 00000000..38ad1bc8 --- /dev/null +++ b/official/docs/node/current/trackers/batch.js @@ -0,0 +1,11 @@ +const EasyPostClient = require('@easypost/api'); + +const client = new EasyPostClient('EASYPOST_API_KEY'); + +(async () => { + const trackers = await client.Tracker.retrieveBatch({ + tracking_codes: ['EZ1000000001', 'EZ1000000002'], + }); + + console.log(trackers); +})(); diff --git a/official/docs/php/current/trackers/batch.php b/official/docs/php/current/trackers/batch.php new file mode 100644 index 00000000..6959e5dd --- /dev/null +++ b/official/docs/php/current/trackers/batch.php @@ -0,0 +1,9 @@ +tracker->retrieveBatch([ + 'tracking_codes' => ['EZ1000000001', 'EZ1000000002'], +]); + +echo $trackers; diff --git a/official/docs/python/current/trackers/batch.py b/official/docs/python/current/trackers/batch.py new file mode 100644 index 00000000..7a5cc3f6 --- /dev/null +++ b/official/docs/python/current/trackers/batch.py @@ -0,0 +1,9 @@ +import easypost + +client = easypost.EasyPostClient("EASYPOST_API_KEY") + +trackers = client.tracker.retrieve_batch( + tracking_codes=["EZ1000000001", "EZ1000000002"], +) + +print(trackers) diff --git a/official/docs/responses/billing/create-referral-token.json b/official/docs/responses/billing/create-referral-token.json new file mode 100644 index 00000000..e40807f6 --- /dev/null +++ b/official/docs/responses/billing/create-referral-token.json @@ -0,0 +1,3 @@ +{ + "public_key": "pk_..." +} diff --git a/official/docs/responses/carrier-metadata/retrieve.json b/official/docs/responses/carrier-metadata/retrieve.json index 8ee3df89..ad534136 100644 --- a/official/docs/responses/carrier-metadata/retrieve.json +++ b/official/docs/responses/carrier-metadata/retrieve.json @@ -7,7 +7,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["6in x 4.5in x 0.016in"], + "dimensions": [ + "6in x 4.5in x 0.016in" + ], "human_readable": null, "max_weight": null, "name": "Card" @@ -15,7 +17,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["11.5in x 6.125in x 0.25in"], + "dimensions": [ + "11.5in x 6.125in x 0.25in" + ], "human_readable": null, "max_weight": null, "name": "Letter" @@ -23,7 +27,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["11.5in x 6.125in x 0.75in"], + "dimensions": [ + "11.5in x 6.125in x 0.75in" + ], "human_readable": null, "max_weight": null, "name": "Flat" @@ -31,7 +37,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["12.5in x 9.5in"], + "dimensions": [ + "12.5in x 9.5in" + ], "human_readable": null, "max_weight": null, "name": "FlatRateEnvelope" @@ -39,7 +47,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["15in x 9.5in"], + "dimensions": [ + "15in x 9.5in" + ], "human_readable": null, "max_weight": null, "name": "FlatRateLegalEnvelope" @@ -47,7 +57,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["12.5in x 9.5in"], + "dimensions": [ + "12.5in x 9.5in" + ], "human_readable": null, "max_weight": null, "name": "FlatRatePaddedEnvelope" @@ -55,7 +67,10 @@ { "carrier": "usps", "description": null, - "dimensions": ["10in x 5in", "12.5in x 9.5in"], + "dimensions": [ + "10in x 5in", + "12.5in x 9.5in" + ], "human_readable": null, "max_weight": null, "name": "FlatRateWindowEnvelope" @@ -63,7 +78,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["12.5in x 9.5in"], + "dimensions": [ + "12.5in x 9.5in" + ], "human_readable": null, "max_weight": null, "name": "FlatRateCardboardEnvelope" @@ -71,7 +88,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["10in x 6in"], + "dimensions": [ + "10in x 6in" + ], "human_readable": null, "max_weight": null, "name": "SmallFlatRateEnvelope" @@ -79,7 +98,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["Varies based on service level"], + "dimensions": [ + "Varies based on service level" + ], "human_readable": null, "max_weight": null, "name": "Parcel" @@ -87,7 +108,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["Varies based on service level"], + "dimensions": [ + "Varies based on service level" + ], "human_readable": null, "max_weight": 320.0, "name": "SoftPack" @@ -95,7 +118,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["8.6875in x 5.4375in x 1.75in"], + "dimensions": [ + "8.6875in x 5.4375in x 1.75in" + ], "human_readable": null, "max_weight": null, "name": "SmallFlatRateBox" @@ -103,7 +128,10 @@ { "carrier": "usps", "description": null, - "dimensions": ["11.25in x 8.75in x 6in", "14.125in x 12in x 3.5in"], + "dimensions": [ + "11.25in x 8.75in x 6in", + "14.125in x 12in x 3.5in" + ], "human_readable": null, "max_weight": null, "name": "MediumFlatRateBox" @@ -111,7 +139,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["12.25in x 12in x 6in"], + "dimensions": [ + "12.25in x 12in x 6in" + ], "human_readable": null, "max_weight": null, "name": "LargeFlatRateBox" @@ -119,7 +149,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["12.25in x 12.25in x 6in"], + "dimensions": [ + "12.25in x 12.25in x 6in" + ], "human_readable": null, "max_weight": null, "name": "LargeFlatRateBoxAPOFPO" @@ -180,7 +212,9 @@ { "carrier": "usps", "description": "1-3 business days", - "dimensions": ["Combined Length and Girth < 108in"], + "dimensions": [ + "Combined Length and Girth < 108in" + ], "human_readable": "Priority Mail\u00ae", "max_weight": 1120.0, "name": "Priority" @@ -188,7 +222,9 @@ { "carrier": "usps", "description": "1-2 days", - "dimensions": ["Combined Length and Girth < 108in"], + "dimensions": [ + "Combined Length and Girth < 108in" + ], "human_readable": "Priority Mail Express\u00ae", "max_weight": 1120.0, "name": "Express" @@ -196,7 +232,9 @@ { "carrier": "usps", "description": "2-5 days", - "dimensions": ["Combined Length and Girth < 130in"], + "dimensions": [ + "Combined Length and Girth < 130in" + ], "human_readable": "USPS Ground Advantage\u00ae", "max_weight": 70.0, "name": "GroundAdvantage" @@ -204,7 +242,9 @@ { "carrier": "usps", "description": "2-8 business days", - "dimensions": ["Combined Length and Girth < 108in"], + "dimensions": [ + "Combined Length and Girth < 108in" + ], "human_readable": "Library Mail\u00ae", "max_weight": 1120.0, "name": "LibraryMail" @@ -212,7 +252,9 @@ { "carrier": "usps", "description": "2-8 business days", - "dimensions": ["Combined Length and Girth < 108in"], + "dimensions": [ + "Combined Length and Girth < 108in" + ], "human_readable": "Media Mail\u00ae", "max_weight": 1120.0, "name": "MediaMail" @@ -220,7 +262,9 @@ { "carrier": "usps", "description": null, - "dimensions": ["Must be rectangular, otherwise an additional charge may apply."], + "dimensions": [ + "Must be rectangular, otherwise an additional charge may apply." + ], "human_readable": "First-Class Mail International\u00ae", "max_weight": 15.994, "name": "FirstClassMailInternational" @@ -240,7 +284,9 @@ { "carrier": "usps", "description": "6-10 business days", - "dimensions": ["Combined Length and Girth < 108in"], + "dimensions": [ + "Combined Length and Girth < 108in" + ], "human_readable": "Priority Mail International\u00ae", "max_weight": 1120.0, "name": "PriorityMailInternational" @@ -248,7 +294,9 @@ { "carrier": "usps", "description": "3-5 business days", - "dimensions": ["Combined Length and Girth < 108in"], + "dimensions": [ + "Combined Length and Girth < 108in" + ], "human_readable": "Priority Mail Express International\u00ae", "max_weight": 1120.0, "name": "ExpressMailInternational" diff --git a/official/docs/responses/insurance/refund.json b/official/docs/responses/insurance/refund.json index c76f7adc..e4d245f7 100644 --- a/official/docs/responses/insurance/refund.json +++ b/official/docs/responses/insurance/refund.json @@ -167,7 +167,9 @@ "charged": true, "refunded": false }, - "messages": ["Insurance was cancelled by the user."], + "messages": [ + "Insurance was cancelled by the user." + ], "created_at": "2025-05-12T19:40:17Z", "updated_at": "2025-05-12T19:40:18Z" } diff --git a/official/docs/responses/referral-customers/refund-by-amount.json b/official/docs/responses/referral-customers/refund-by-amount.json index 50dd7a69..ba81253e 100644 --- a/official/docs/responses/referral-customers/refund-by-amount.json +++ b/official/docs/responses/referral-customers/refund-by-amount.json @@ -1,6 +1,9 @@ { "refunded_amount": 2000, "payment_log_id": "paylog_...", - "refunded_payment_logs": ["paylog...", "paylog..."], + "refunded_payment_logs": [ + "paylog...", + "paylog..." + ], "errors": [] } diff --git a/official/docs/responses/referral-customers/refund-by-payment-log.json b/official/docs/responses/referral-customers/refund-by-payment-log.json index 50dd7a69..ba81253e 100644 --- a/official/docs/responses/referral-customers/refund-by-payment-log.json +++ b/official/docs/responses/referral-customers/refund-by-payment-log.json @@ -1,6 +1,9 @@ { "refunded_amount": 2000, "payment_log_id": "paylog_...", - "refunded_payment_logs": ["paylog...", "paylog..."], + "refunded_payment_logs": [ + "paylog...", + "paylog..." + ], "errors": [] } diff --git a/official/docs/responses/scan-form/create.json b/official/docs/responses/scan-form/create.json index a1c2082e..9437d600 100644 --- a/official/docs/responses/scan-form/create.json +++ b/official/docs/responses/scan-form/create.json @@ -3,7 +3,9 @@ "object": "ScanForm", "created_at": "2025-05-09T20:40:04Z", "updated_at": "2025-05-09T20:40:04Z", - "tracking_codes": ["9405500208303109884151"], + "tracking_codes": [ + "9405500208303109884151" + ], "address": { "id": "adr_c8c8ddad2d1511f0b56e3cecef1b359e", "object": "Address", diff --git a/official/docs/responses/scan-form/list.json b/official/docs/responses/scan-form/list.json index 77713709..7084d970 100644 --- a/official/docs/responses/scan-form/list.json +++ b/official/docs/responses/scan-form/list.json @@ -5,7 +5,9 @@ "object": "ScanForm", "created_at": "2025-05-09T20:40:06Z", "updated_at": "2025-05-09T20:40:06Z", - "tracking_codes": ["9405500208303109884168"], + "tracking_codes": [ + "9405500208303109884168" + ], "address": { "id": "adr_c9b1e7bd2d1511f093deac1f6bc539aa", "object": "Address", diff --git a/official/docs/responses/scan-form/retrieve.json b/official/docs/responses/scan-form/retrieve.json index b9f2f1ff..6a073494 100644 --- a/official/docs/responses/scan-form/retrieve.json +++ b/official/docs/responses/scan-form/retrieve.json @@ -3,7 +3,9 @@ "object": "ScanForm", "created_at": "2025-05-09T20:40:06Z", "updated_at": "2025-05-09T20:40:06Z", - "tracking_codes": ["9405500208303109884168"], + "tracking_codes": [ + "9405500208303109884168" + ], "address": { "id": "adr_c9b1e7bd2d1511f093deac1f6bc539aa", "object": "Address", diff --git a/official/docs/responses/shipments/list.json b/official/docs/responses/shipments/list.json index 90ae6949..f26eb715 100644 --- a/official/docs/responses/shipments/list.json +++ b/official/docs/responses/shipments/list.json @@ -184,7 +184,9 @@ "object": "ScanForm", "created_at": "2025-05-09T20:40:06Z", "updated_at": "2025-05-09T20:40:06Z", - "tracking_codes": ["9405500208303109884168"], + "tracking_codes": [ + "9405500208303109884168" + ], "address": { "id": "adr_c9b1e7bd2d1511f093deac1f6bc539aa", "object": "Address", diff --git a/official/docs/responses/trackers/batch.json b/official/docs/responses/trackers/batch.json new file mode 100644 index 00000000..cc8d0f3b --- /dev/null +++ b/official/docs/responses/trackers/batch.json @@ -0,0 +1,85 @@ +{ + "trackers": [ + { + "id": "trk_b5aa9436e34742c69e9fda00dda7b640", + "object": "Tracker", + "mode": "test", + "tracking_code": "EZ1000000001", + "status": "pre_transit", + "status_detail": "status_update", + "created_at": "2026-09-30T19:55:20Z", + "updated_at": "2026-09-30T19:55:20Z", + "signed_by": null, + "weight": null, + "est_delivery_date": "2026-09-30T19:55:20Z", + "shipment_id": null, + "carrier": "USPS", + "tracking_details": [ + { + "object": "TrackingDetail", + "message": "Pre-Shipment Info Sent to USPS", + "description": "", + "status": "pre_transit", + "status_detail": "status_update", + "datetime": "2026-08-30T19:55:20Z", + "source": "USPS", + "carrier_code": "", + "tracking_location": { + "object": "TrackingLocation", + "city": null, + "state": null, + "country": null, + "zip": null + }, + "est_delivery_date": null, + "datetime_local": "2026-08-30T19:55:20+00:00", + "geocode_confidence": "LOW" + }, + { + "object": "TrackingDetail", + "message": "Shipping Label Created", + "description": "", + "status": "pre_transit", + "status_detail": "status_update", + "datetime": "2026-08-31T08:32:20Z", + "source": "USPS", + "carrier_code": "", + "tracking_location": { + "object": "TrackingLocation", + "city": "HOUSTON", + "state": "TX", + "country": null, + "zip": "77063" + }, + "est_delivery_date": null, + "datetime_local": "2026-08-31T08:32:20+00:00", + "geocode_confidence": "LOW" + } + ], + "fees": [], + "carrier_detail": { + "object": "CarrierDetail", + "service": "First-Class Package Service", + "container_type": null, + "est_delivery_date_local": null, + "est_delivery_time_local": null, + "origin_location": "HOUSTON TX, 77001", + "origin_tracking_location": { + "object": "TrackingLocation", + "city": "HOUSTON", + "state": "TX", + "country": null, + "zip": "77063" + }, + "destination_location": "CHARLESTON SC, 29401", + "destination_tracking_location": null, + "guaranteed_delivery_date": null, + "alternate_identifier": null, + "initial_delivery_attempt": null + }, + "delivery_evidence": [], + "public_url": "https://track.easypost.com/djE6dHJrX2I1YWE5NDM2ZTM0NzQyYzY5ZTlmZGEwMGRkYTdiNjQw" + } + ], + "has_more": false +} diff --git a/official/docs/ruby/current/trackers/batch.rb b/official/docs/ruby/current/trackers/batch.rb new file mode 100644 index 00000000..b9f1fbb1 --- /dev/null +++ b/official/docs/ruby/current/trackers/batch.rb @@ -0,0 +1,9 @@ +require 'easypost' + +client = EasyPost::Client.new(api_key: 'EASYPOST_API_KEY') + +trackers = client.tracker.retrieve_batch( + tracking_codes: %w[EZ1000000001 EZ1000000002], +) + +puts trackers diff --git a/style_guides/node/.prettierignore b/style_guides/node/.prettierignore index 30e721b3..354ddfb1 100644 --- a/style_guides/node/.prettierignore +++ b/style_guides/node/.prettierignore @@ -4,13 +4,19 @@ .pytest_cache .ruff_cache .tmp -/docs cassettes composer.json composer.lock coverage dist/ examples +official/docs/csharp +official/docs/golang +official/docs/java +official/docs/php +official/docs/python +official/docs/responses +official/docs/ruby official/fixtures/ package-lock.json style_guides/ diff --git a/tools/build_doc_json_responses/Makefile b/tools/build_doc_json_responses/Makefile deleted file mode 100644 index 5d67fd96..00000000 --- a/tools/build_doc_json_responses/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -PYTHON_BINARY := python3 -VIRTUAL_ENV := venv -VIRTUAL_BIN := $(VIRTUAL_ENV)/bin -SOURCE_CODE_DIR := builder -RESPONSES_DIR := responses -TEST_DIR := tests - -## help - Display help about make targets for this Makefile -help: - @cat Makefile | grep '^## ' --color=never | cut -c4- | sed -e "`printf 's/ - /\t- /;'`" | column -s "`printf '\t'`" -t - -## build - Build the Python package needed to run the project -build: - $(VIRTUAL_BIN)/python setup.py sdist bdist_wheel - -## clean - Remove the virtual environment and clear out .pyc files -clean: - rm -rf $(VIRTUAL_ENV) dist/ build/ *.egg-info/ .pytest_cache .mypy_cache - find . -name '*.pyc' -delete - -## black - Runs the Black Python formatter against the project -black: - $(VIRTUAL_BIN)/black $(SOURCE_CODE_DIR)/ $(TEST_DIR)/ - -## black-check - Checks if the project is formatted correctly against the Black rules -black-check: - $(VIRTUAL_BIN)/black $(SOURCE_CODE_DIR)/ $(TEST_DIR)/ --check - -## flake8 - Runs the Flake8 linter against the project -flake8: - $(VIRTUAL_BIN)/flake8 $(SOURCE_CODE_DIR)/ $(TEST_DIR)/ - -## generate - Generate the JSON response bodies -generate: - $(VIRTUAL_BIN)/pytest - -## install - Install the project locally -install: - $(PYTHON_BINARY) -m venv $(VIRTUAL_ENV) - $(VIRTUAL_BIN)/pip install -e ."[dev]" - git submodule init - git submodule update - -## isort - Sorts imports throughout the project -isort: - $(VIRTUAL_BIN)/isort $(SOURCE_CODE_DIR)/ $(TEST_DIR)/ - -## isort-check - Checks that imports throughout the project are sorted correctly -isort-check: - $(VIRTUAL_BIN)/isort $(SOURCE_CODE_DIR)/ $(TEST_DIR)/ --check-only - -## lint - Runs all linting and formatting tools against the project -lint: black-check isort-check flake8 - -## lint-fix - Automatically fixes linting and formatting issues -lint-fix: black isort - -.PHONY: help build clean black black-check flake8 generate install isort isort-check lint lint-fix diff --git a/tools/build_doc_json_responses/builder/snippets.py b/tools/build_doc_json_responses/builder/snippets.py index 779eb4a8..f817d1d4 100644 --- a/tools/build_doc_json_responses/builder/snippets.py +++ b/tools/build_doc_json_responses/builder/snippets.py @@ -2,14 +2,10 @@ import os from typing import ( Any, - Dict, - Optional, - Tuple, ) import yaml - ALL_RESOURCES = { "addresses", "api-keys", @@ -49,7 +45,7 @@ RESPONSES_DIR = os.path.join("..", "..", "official", "docs", "responses") -def build_response_snippet(interaction_index: Optional[int] = 0, objects_to_persist: Optional[int] = None): +def build_response_snippet(interaction_index: int | None = 0, objects_to_persist: int | None = None): """Builds the response snippet from a recorded VCR interaction.""" create_dir(os.path.join(RESPONSES_DIR)) @@ -63,9 +59,9 @@ def build_response_snippet(interaction_index: Optional[int] = 0, objects_to_pers ) # Assert the standalone snippet actually got saved, fail if not - assert os.path.exists( - os.path.join(RESPONSES_DIR, response_snippet_folder, bare_snippet_name) - ), f"{bare_snippet_name} standalone snippet file missing!" + assert os.path.exists(os.path.join(RESPONSES_DIR, response_snippet_folder, bare_snippet_name)), ( + f"{bare_snippet_name} standalone snippet file missing!" + ) def create_dir(dir_name: str): @@ -74,21 +70,18 @@ def create_dir(dir_name: str): os.mkdir(dir_name) -def extract_response_from_cassette(cassette_filename: str, interaction_index: Optional[int] = 0) -> Any: +def extract_response_from_cassette(cassette_filename: str, interaction_index: int | None = 0) -> Any: """Opens a single cassette file and extracts the response content.""" cassette_file = os.path.join("tests", "cassettes", cassette_filename) if not os.path.exists(cassette_file): raise FileNotFoundError(f"{cassette_filename} not found, run the tests again.") with open(cassette_file, "r") as cassette: - try: - cassette_data = yaml.safe_load(cassette) - for key, _ in cassette_data.items(): - if key == "interactions": - response_content = cassette_data[key][interaction_index]["response"]["body"]["string"] - response = response_content if response_content else "{}" - except yaml.YAMLError: - raise + cassette_data = yaml.safe_load(cassette) + for key in cassette_data: + if key == "interactions": + response_content = cassette_data[key][interaction_index]["response"]["body"]["string"] + response = response_content if response_content else "{}" return response @@ -96,18 +89,28 @@ def extract_response_from_cassette(cassette_filename: str, interaction_index: Op def _setup_saving_response_snippet(response_snippet_filename: str): """Reusable helper to setup the logic to save a standalone response snippet.""" - bare_snippet_name = response_snippet_filename.replace("test_", "").replace(".yaml", ".json") - split_resource_name = bare_snippet_name.split("_") - first_resource_name = split_resource_name[0] - second_resource_name = split_resource_name[1] + normalized_name = response_snippet_filename.replace("test_", "").replace(".yaml", "") + split_name = normalized_name.split("_") + + first_resource_name = split_name[0] + second_resource_name = split_name[1] if len(split_name) > 1 else "" first_and_second_resource_name = f"{first_resource_name}-{second_resource_name}" - resource_name = ( - first_resource_name if first_and_second_resource_name not in ALL_RESOURCES else first_and_second_resource_name - ) - # Setup the names like the website wants it + # Some resources are two words and represented with hyphens in docs paths (eg: scan-form). + if first_and_second_resource_name in ALL_RESOURCES: + resource_name = first_and_second_resource_name + action_start_index = 2 + else: + resource_name = first_resource_name + action_start_index = 1 + + action_name = "-".join(split_name[action_start_index:]) + if not action_name: + raise ValueError(f"Could not parse action name from: {response_snippet_filename}") + + # Response filenames should only contain the action name (no resource prefix). response_snippet_folder = resource_name.replace("_", "-") - bare_snippet_name = bare_snippet_name.replace("_", "-") + bare_snippet_name = f"{action_name.replace('_', '-')}.json" create_dir(os.path.join(RESPONSES_DIR, response_snippet_folder)) @@ -117,8 +120,8 @@ def _setup_saving_response_snippet(response_snippet_filename: str): def save_response_snippet( response_snippet_filename: str, response_snippet_content: Any, - objects_to_persist: Optional[int] = None, -) -> Tuple[str, str]: + objects_to_persist: int | None = None, +) -> tuple[str, str]: """Saves the response content of a cassette to a standalone snippet file.""" response_snippet_folder, bare_snippet_name = _setup_saving_response_snippet(response_snippet_filename) @@ -137,14 +140,12 @@ def save_response_snippet( return response_snippet_folder, bare_snippet_name -def save_raw_json(response_snippet_filename: str, response_dict: Dict[str, Any]): +def save_raw_json(response_snippet_filename: str, response_dict: dict[str, Any]): """Saves a raw response dictionary to a standalone snippet file (used for hard-coded responses that cannot easily be plugged into a test suite (eg: Billing functions). """ response_snippet_folder, bare_snippet_name = _setup_saving_response_snippet(response_snippet_filename) - bare_snippet_name = f"{bare_snippet_name}.json" - with open(os.path.join(RESPONSES_DIR, response_snippet_folder, bare_snippet_name), "w") as response_snippet_file: json.dump(response_dict, response_snippet_file, indent=2) diff --git a/tools/build_doc_json_responses/justfile b/tools/build_doc_json_responses/justfile new file mode 100644 index 00000000..b77d5155 --- /dev/null +++ b/tools/build_doc_json_responses/justfile @@ -0,0 +1,32 @@ +PYTHON_BINARY := "python3" +VIRTUAL_ENV := "venv" +VIRTUAL_BIN := VIRTUAL_ENV / "bin" +SOURCE_CODE_DIR := "builder" +RESPONSES_DIR := "responses" +TEST_DIR := "tests" + +# clean - Remove the virtual environment and clear out .pyc files +clean: + rm -rf {{VIRTUAL_ENV}} dist/ build/ *.egg-info/ .pytest_cache .mypy_cache + find . -name '*.pyc' -delete + +# generate - Generate the JSON response bodies +generate: + {{VIRTUAL_BIN}}/pytest + +# install - Install the project locally +install: + {{PYTHON_BINARY}} -m venv {{VIRTUAL_ENV}} + {{VIRTUAL_BIN}}/pip install -e ."[dev]" + git submodule init + git submodule update + +# lint - Run the linter and formatter checks +lint: + {{VIRTUAL_BIN}}/ruff check {{SOURCE_CODE_DIR}}/ {{TEST_DIR}}/ + {{VIRTUAL_BIN}}/ruff format --check {{SOURCE_CODE_DIR}}/ {{TEST_DIR}}/ + +# Fix lint issues +lint-fix: + {{VIRTUAL_BIN}}/ruff check --fix {{SOURCE_CODE_DIR}}/ {{TEST_DIR}}/ + {{VIRTUAL_BIN}}/ruff format {{SOURCE_CODE_DIR}}/ {{TEST_DIR}}/ diff --git a/tools/build_doc_json_responses/pyproject.toml b/tools/build_doc_json_responses/pyproject.toml new file mode 100644 index 00000000..61b96a2b --- /dev/null +++ b/tools/build_doc_json_responses/pyproject.toml @@ -0,0 +1,21 @@ +[build-system] +requires = ["setuptools>=61", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "easypost_responses" +version = "0.1.0" +description = "" +readme = "README.md" +requires-python = ">=3.10,<4" +authors = [{ name = "EasyPost", email = "support@easypost.com" }] +dependencies = ["easypost==10.*", "python-dotenv"] + +[project.urls] +Homepage = "https://easypost.com/" + +[project.optional-dependencies] +dev = ["ruff==0.16.*", "pytest-vcr==1.*", "pytest==8.*", "vcrpy==7.*"] + +[tool.setuptools.packages.find] +exclude = ["examples", "tests"] diff --git a/tools/build_doc_json_responses/setup.py b/tools/build_doc_json_responses/setup.py deleted file mode 100644 index cf0482e7..00000000 --- a/tools/build_doc_json_responses/setup.py +++ /dev/null @@ -1,45 +0,0 @@ -from setuptools import ( - find_packages, - setup, -) - - -REQUIREMENTS = [ - "easypost==10.*", - "python-dotenv", -] - -DEV_REQUIREMENTS = [ - "black==25.*", - "flake8==7.*", - "isort==6.*", - "pytest-vcr==1.*", - "pytest==8.*", - "vcrpy==7.*", -] - -with open("README.md", encoding="utf-8") as f: - long_description = f.read() - -setup( - name="easypost_responses", - version="0.1.0", - description="", - author="EasyPost", - author_email="support@easypost.com", - url="https://easypost.com/", - packages=find_packages( - exclude=[ - "examples", - "tests", - ] - ), - install_requires=REQUIREMENTS, - extras_require={ - "dev": DEV_REQUIREMENTS, - }, - test_suite="test", - long_description=long_description, - long_description_content_type="text/markdown", - python_requires=">=3.9, <4", -) diff --git a/tools/build_doc_json_responses/tests/conftest.py b/tools/build_doc_json_responses/tests/conftest.py index bc9ffdeb..3a30d4ac 100644 --- a/tools/build_doc_json_responses/tests/conftest.py +++ b/tools/build_doc_json_responses/tests/conftest.py @@ -3,16 +3,12 @@ import os from typing import ( Any, - Dict, - List, - Tuple, ) import easypost import pytest from dotenv import load_dotenv - load_dotenv() EASYPOST_TEST_API_KEY = os.getenv("EASYPOST_TEST_API_KEY") @@ -20,11 +16,11 @@ PARTNER_USER_PROD_API_KEY = os.getenv("PARTNER_USER_PROD_API_KEY", "123") REFERRAL_CUSTOMER_PROD_API_KEY = os.getenv("REFERRAL_CUSTOMER_PROD_API_KEY", "123") -OVERWRITE = "all" if os.getenv("OVERWRITE", False) else "once" +OVERWRITE = "all" if os.getenv("OVERWRITE") else "once" SCRUBBED_STRING = "" -SCRUBBED_ARRAY: List = [] -SCRUBBED_DICT: Dict = {} +SCRUBBED_ARRAY: list = [] +SCRUBBED_DICT: dict = {} @pytest.fixture(scope="session") @@ -56,7 +52,7 @@ def vcr_config(): } -def scrub_response_bodies(scrubbers: List[Tuple[str, Any]]) -> Any: +def scrub_response_bodies(scrubbers: list[tuple[str, Any]]) -> Any: """Scrub sensitive data from response bodies prior to recording the cassette.""" def before_record_response(response: Any) -> Any: @@ -70,7 +66,7 @@ def before_record_response(response: Any) -> Any: response["body"]["string"] = json.dumps(response_body).encode() return response - def scrub_data(data: Any, scrubber: Tuple[str, Any]) -> Any: + def scrub_data(data: Any, scrubber: tuple[str, Any]) -> Any: """Scrub data from a cassette recursively.""" key = scrubber[0] replacement = scrubber[1] @@ -133,7 +129,7 @@ def referral_customer_prod_client(): @pytest.fixture() def next_weekday(): """Returns the next weekday as a string in YYYY-MM-DD format.""" - tomorrow = datetime.date.today() + datetime.timedelta(days=1) + tomorrow = datetime.datetime.now(tz=datetime.timezone.utc).date() + datetime.timedelta(days=1) if tomorrow.weekday() == 5: # Tomorrow is Saturday # Return next Monday return (tomorrow + datetime.timedelta(days=2)).strftime("%Y-%m-%d") @@ -148,7 +144,7 @@ def next_weekday(): @pytest.fixture() def next_week(): """Returns the day a week from now as a string in YYYY-MM-DD format.""" - today = datetime.date.today() + today = datetime.datetime.now(tz=datetime.timezone.utc).date() if today.weekday() == 5: # Tomorrow is Saturday # Return next Monday return (today + datetime.timedelta(days=9)).strftime("%Y-%m-%d") diff --git a/tools/build_doc_json_responses/tests/test_address.py b/tools/build_doc_json_responses/tests/test_address.py index 0b2c566a..db6f9f20 100644 --- a/tools/build_doc_json_responses/tests/test_address.py +++ b/tools/build_doc_json_responses/tests/test_address.py @@ -13,7 +13,7 @@ def test_addresses_create(test_client, address_create): def test_addresses_create_and_verify(test_client, address_incorrect): try: test_client.address.create_and_verify(**address_incorrect) - except Exception: + except Exception: # noqa: BLE001, S110 # This will fail because the address is incorrect; this is expected. pass @@ -34,7 +34,7 @@ def test_addresses_verify_strict_param(test_client, address_incorrect): try: test_client.address.create(**address_incorrect) - except Exception: + except Exception: # noqa: BLE001, S110 # This will fail because the address is incorrect; this is expected. pass diff --git a/tools/build_doc_json_responses/tests/test_billing.py b/tools/build_doc_json_responses/tests/test_billing.py index d688242d..261f1441 100644 --- a/tools/build_doc_json_responses/tests/test_billing.py +++ b/tools/build_doc_json_responses/tests/test_billing.py @@ -58,21 +58,6 @@ def test_billing_create_referral_token(): save_raw_json(test_name, response_dict) -def test_billing_create_ep_credit_card(): - response_dict = { - "id": "card_...", - "object": "CreditCard", - "name": None, - "last4": "1234", - "exp_month": 1, - "exp_year": 2025, - "brand": "Visa", - } - - test_name = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] - save_raw_json(test_name, response_dict) - - @pytest.mark.vcr() def test_billing_create_stripe_bank_account_secret(prod_client): prod_client.beta_referral_customer.create_bank_account_client_secret() diff --git a/tools/build_doc_json_responses/tests/test_luma.py b/tools/build_doc_json_responses/tests/test_luma.py index 45c06521..1317b189 100644 --- a/tools/build_doc_json_responses/tests/test_luma.py +++ b/tools/build_doc_json_responses/tests/test_luma.py @@ -607,7 +607,7 @@ def test_luma_buy(): }, ], "matching_rule_idx": 0, - "ruleset_description": "Buy the cheapest rate that satisfies all of the following:\nRule 0\n - Deliver by user input day of deliver_by_date - Transit day confidence >= 90\nOtherwise buy the cheapest rate", # noqa + "ruleset_description": "Buy the cheapest rate that satisfies all of the following:\nRule 0\n - Deliver by user input day of deliver_by_date - Transit day confidence >= 90\nOtherwise buy the cheapest rate", }, "postage_label": { "object": "PostageLabel", @@ -621,7 +621,7 @@ def test_luma_buy(): "label_size": "4x7", "label_type": "default", "label_file_type": "image/png", - "label_url": "https://easypost-files.s3.us-west-2.amazonaws.com/files/postage_label/20250205/e82ceb24e620684ea7829d0f6866c2ba5f.png", # noqa + "label_url": "https://easypost-files.s3.us-west-2.amazonaws.com/files/postage_label/20250205/e82ceb24e620684ea7829d0f6866c2ba5f.png", "label_pdf_url": None, "label_zpl_url": None, "label_epl2_url": None, @@ -818,13 +818,14 @@ def test_luma_info(): }, ], "matching_rule_idx": 0, - "ruleset_description": "Buy the cheapest rate that satisfies all of the following:\nRule 0\n - Deliver by user input day of deliver_by_date - Transit day confidence >= 90\nOtherwise buy the cheapest rate", # noqa + "ruleset_description": "Buy the cheapest rate that satisfies all of the following:\nRule 0\n - Deliver by user input day of deliver_by_date - Transit day confidence >= 90\nOtherwise buy the cheapest rate", } } test_name = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] save_raw_json(test_name, response_dict) + def test_luma_one_call_buy(): # TODO: Luma functions not implemented in Python lib yet, replace when live response_dict = { @@ -852,7 +853,7 @@ def test_luma_one_call_buy(): "residential": None, "federal_tax_id": None, "state_tax_id": None, - "verifications": {} + "verifications": {}, }, "from_address": { "id": "adr_8a6edaed358f11f099333cecef1b359e", @@ -874,7 +875,7 @@ def test_luma_one_call_buy(): "residential": None, "federal_tax_id": None, "state_tax_id": None, - "verifications": {} + "verifications": {}, }, "parcel": { "id": "prcl_fbcd37ff5d1b46a7bf7a97c8d737c3a6", @@ -886,7 +887,7 @@ def test_luma_one_call_buy(): "height": 10.0, "predefined_package": None, "weight": 32.0, - "mode": "test" + "mode": "test", }, "customs_info": None, "forms": [], @@ -911,151 +912,133 @@ def test_luma_one_call_buy(): "delivery_date_guaranteed": False, "est_delivery_days": 3, "shipment_id": "shp_92521948d13a46c0a37acc02c7a8eebc", - "carrier_account_id": "ca_1" + "carrier_account_id": "ca_1", }, "luma_info": { "ai_results": [ - { - "carrier": "USPS", - "meets_ruleset_requirements": False, - "predicted_deliver_by_date": "2025-05-27", - "predicted_deliver_days": 7, - "rate_id": "rate_0114e549b7394ce59145fa70f456b504", - "rate_usd": "23.85", - "service": "GroundAdvantage" - }, - { - "carrier": "USPS", - "meets_ruleset_requirements": True, - "predicted_deliver_by_date": "2025-05-23", - "predicted_deliver_days": 3, - "rate_id": "rate_a99eed3f08a44ad59feb6dfcd5dea2d5", - "rate_usd": "135.45", - "service": "Express" - }, - { - "carrier": "USPS", - "meets_ruleset_requirements": True, - "predicted_deliver_by_date": "2025-05-24", - "predicted_deliver_days": 4, - "rate_id": "rate_cead4ccaacc146a0be768ba05d748898", - "rate_usd": "41.44", - "service": "Priority" - }, - { - "carrier": "UPSDAP", - "meets_ruleset_requirements": True, - "predicted_deliver_by_date": "2025-05-21", - "predicted_deliver_days": 1, - "rate_id": "rate_7b87f5e086ee4ca18a6396c8c50ed218", - "rate_usd": "94.45", - "service": "NextDayAir" - }, - { - "carrier": "UPSDAP", - "meets_ruleset_requirements": True, - "predicted_deliver_by_date": "2025-05-21", - "predicted_deliver_days": 1, - "rate_id": "rate_7b221d5778c3456baab18cc14898808e", - "rate_usd": "124.45", - "service": "NextDayAirEarlyAM" - }, - { - "carrier": "UPSDAP", - "meets_ruleset_requirements": True, - "predicted_deliver_by_date": "2025-05-21", - "predicted_deliver_days": 1, - "rate_id": "rate_2dbcf54f5fc34b82aba06311371fb380", - "rate_usd": "87.91", - "service": "NextDayAirSaver" - }, - { - "carrier": "UPSDAP", - "meets_ruleset_requirements": True, - "predicted_deliver_by_date": "2025-05-22", - "predicted_deliver_days": 2, - "rate_id": "rate_368af0549232430fb71305d441aad6f2", - "rate_usd": "52.98", - "service": "2ndDayAir" - }, - { - "carrier": "UPSDAP", - "meets_ruleset_requirements": True, - "predicted_deliver_by_date": "2025-05-22", - "predicted_deliver_days": 2, - "rate_id": "rate_99d99ac69d6f4d5f99983572e6dd03af", - "rate_usd": "60.79", - "service": "2ndDayAirAM" - }, - { - "carrier": "UPSDAP", - "meets_ruleset_requirements": True, - "predicted_deliver_by_date": "2025-05-23", - "predicted_deliver_days": 3, - "rate_id": "rate_a8b60fd7f1e14dd0afcb0fe894f5492d", - "rate_usd": "38.19", - "service": "3DaySelect" - }, - { - "carrier": "UPSDAP", - "meets_ruleset_requirements": False, - "predicted_deliver_by_date": "2025-05-28", - "predicted_deliver_days": 8, - "rate_id": "rate_da501c08b53347f4824392bc5f7a2a34", - "rate_usd": "25.04", - "service": "Ground" - } - ], - "matching_rule_idx": 0, - "ruleset_description": "Buy the cheapest rate that satisfies all of the following:\nRule 0\n - Deliver by user input day of deliver_by_date - Transit day confidence >= 90\nOtherwise buy the cheapest rate" - }, - "postage_label": { - "object": "PostageLabel", - "id": "pl_7b3094b609a24032ab5b84111fa2406f", - "created_at": "2025-05-20T15:31:49Z", - "updated_at": "2025-05-20T15:31:49Z", - "date_advance": 0, - "integrated_form": "none", - "label_date": "2025-05-20T15:31:49Z", - "label_resolution": 200, - "label_size": "4x7", - "label_type": "default", - "label_file_type": "image/png", - "label_url": "https://easypost-files.s3.us-west-2.amazonaws.com/files/postage_label/20250520/e8e53bfe41c21845f2995959adb9358ef6.png", - "label_pdf_url": None, - "label_zpl_url": None, - "label_epl2_url": None, - "label_file": None - }, - "messages": [], - "tracking_code": "1ZXXXXXXXXXXXXXXXX", - "fees": [ - { - "object": "Fee", - "type": "LabelFee", - "amount": "0.00000", - "charged": True, - "refunded": False + { + "carrier": "USPS", + "meets_ruleset_requirements": False, + "predicted_deliver_by_date": "2025-05-27", + "predicted_deliver_days": 7, + "rate_id": "rate_0114e549b7394ce59145fa70f456b504", + "rate_usd": "23.85", + "service": "GroundAdvantage", + }, + { + "carrier": "USPS", + "meets_ruleset_requirements": True, + "predicted_deliver_by_date": "2025-05-23", + "predicted_deliver_days": 3, + "rate_id": "rate_a99eed3f08a44ad59feb6dfcd5dea2d5", + "rate_usd": "135.45", + "service": "Express", + }, + { + "carrier": "USPS", + "meets_ruleset_requirements": True, + "predicted_deliver_by_date": "2025-05-24", + "predicted_deliver_days": 4, + "rate_id": "rate_cead4ccaacc146a0be768ba05d748898", + "rate_usd": "41.44", + "service": "Priority", + }, + { + "carrier": "UPSDAP", + "meets_ruleset_requirements": True, + "predicted_deliver_by_date": "2025-05-21", + "predicted_deliver_days": 1, + "rate_id": "rate_7b87f5e086ee4ca18a6396c8c50ed218", + "rate_usd": "94.45", + "service": "NextDayAir", + }, + { + "carrier": "UPSDAP", + "meets_ruleset_requirements": True, + "predicted_deliver_by_date": "2025-05-21", + "predicted_deliver_days": 1, + "rate_id": "rate_7b221d5778c3456baab18cc14898808e", + "rate_usd": "124.45", + "service": "NextDayAirEarlyAM", + }, + { + "carrier": "UPSDAP", + "meets_ruleset_requirements": True, + "predicted_deliver_by_date": "2025-05-21", + "predicted_deliver_days": 1, + "rate_id": "rate_2dbcf54f5fc34b82aba06311371fb380", + "rate_usd": "87.91", + "service": "NextDayAirSaver", + }, + { + "carrier": "UPSDAP", + "meets_ruleset_requirements": True, + "predicted_deliver_by_date": "2025-05-22", + "predicted_deliver_days": 2, + "rate_id": "rate_368af0549232430fb71305d441aad6f2", + "rate_usd": "52.98", + "service": "2ndDayAir", + }, + { + "carrier": "UPSDAP", + "meets_ruleset_requirements": True, + "predicted_deliver_by_date": "2025-05-22", + "predicted_deliver_days": 2, + "rate_id": "rate_99d99ac69d6f4d5f99983572e6dd03af", + "rate_usd": "60.79", + "service": "2ndDayAirAM", + }, + { + "carrier": "UPSDAP", + "meets_ruleset_requirements": True, + "predicted_deliver_by_date": "2025-05-23", + "predicted_deliver_days": 3, + "rate_id": "rate_a8b60fd7f1e14dd0afcb0fe894f5492d", + "rate_usd": "38.19", + "service": "3DaySelect", + }, + { + "carrier": "UPSDAP", + "meets_ruleset_requirements": False, + "predicted_deliver_by_date": "2025-05-28", + "predicted_deliver_days": 8, + "rate_id": "rate_da501c08b53347f4824392bc5f7a2a34", + "rate_usd": "25.04", + "service": "Ground", + }, + ], + "matching_rule_idx": 0, + "ruleset_description": "Buy the cheapest rate that satisfies all of the following:\nRule 0\n - Deliver by user input day of deliver_by_date - Transit day confidence >= 90\nOtherwise buy the cheapest rate", }, - { - "object": "Fee", - "type": "PostageFee", - "amount": "38.19000", - "charged": True, - "refunded": False + "postage_label": { + "object": "PostageLabel", + "id": "pl_7b3094b609a24032ab5b84111fa2406f", + "created_at": "2025-05-20T15:31:49Z", + "updated_at": "2025-05-20T15:31:49Z", + "date_advance": 0, + "integrated_form": "none", + "label_date": "2025-05-20T15:31:49Z", + "label_resolution": 200, + "label_size": "4x7", + "label_type": "default", + "label_file_type": "image/png", + "label_url": "https://easypost-files.s3.us-west-2.amazonaws.com/files/postage_label/20250520/e8e53bfe41c21845f2995959adb9358ef6.png", + "label_pdf_url": None, + "label_zpl_url": None, + "label_epl2_url": None, + "label_file": None, }, - { - "object": "Fee", - "type": "InsuranceFee", - "amount": "0.50000", - "charged": True, - "refunded": False - } - ], - "tax_identifiers": None, - "created_at": "2025-05-20T15:31:46Z", - "updated_at": "2025-05-20T15:31:49Z" -} + "messages": [], + "tracking_code": "1ZXXXXXXXXXXXXXXXX", + "fees": [ + {"object": "Fee", "type": "LabelFee", "amount": "0.00000", "charged": True, "refunded": False}, + {"object": "Fee", "type": "PostageFee", "amount": "38.19000", "charged": True, "refunded": False}, + {"object": "Fee", "type": "InsuranceFee", "amount": "0.50000", "charged": True, "refunded": False}, + ], + "tax_identifiers": None, + "created_at": "2025-05-20T15:31:46Z", + "updated_at": "2025-05-20T15:31:49Z", + } test_name = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] save_raw_json(test_name, response_dict) diff --git a/tools/build_doc_json_responses/tests/test_referral_customer.py b/tools/build_doc_json_responses/tests/test_referral_customer.py index 0a1c8969..e59adb9d 100644 --- a/tools/build_doc_json_responses/tests/test_referral_customer.py +++ b/tools/build_doc_json_responses/tests/test_referral_customer.py @@ -6,7 +6,6 @@ save_raw_json, ) - REFERRAL_USER_PROD_API_KEY = os.getenv("REFERRAL_USER_PROD_API_KEY", "123") diff --git a/tools/build_doc_json_responses/tests/test_tracker.py b/tools/build_doc_json_responses/tests/test_tracker.py index 435a21d6..01467a1e 100644 --- a/tools/build_doc_json_responses/tests/test_tracker.py +++ b/tools/build_doc_json_responses/tests/test_tracker.py @@ -22,3 +22,11 @@ def test_trackers_list(test_client, page_size): test_client.tracker.all(page_size=page_size) build_response_snippet() + + +@pytest.mark.vcr() +def test_trackers_batch(test_client, tracker_create): + tracker1 = test_client.tracker.create(**tracker_create) + test_client.tracker.retrieve_batch(tracking_codes=[tracker1.tracking_code]) + + build_response_snippet(interaction_index=1)