Skip to content

docs: add quote-rename example apps and an SDK test plan - #63

Merged
amitsharma-turbodocx merged 3 commits into
mainfrom
docs/turboquote-rename-examples
Aug 20, 2026
Merged

docs: add quote-rename example apps and an SDK test plan#63
amitsharma-turbodocx merged 3 commits into
mainfrom
docs/turboquote-rename-examples

Conversation

@yacinekahlerras-turbodocx

@yacinekahlerras-turbodocx yacinekahlerras-turbodocx commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Description

TurboQuote's quote naming behaviour changed underneath methods that already exist in all six SDKs. createQuote, updateQuote, duplicateQuote, handleExpiredQuote and sendQuote are unchanged in name, signature and type — so there is nothing to port, and a caller gets the new behaviour with no version bump and no compile error.

That is exactly the kind of change a changelog line doesn't catch, so this adds a runnable check instead.

What's here

packages/*/examples/quote-rename/ — one small app per SDK

Self-contained: creates its own company, contact and quotes, asserts each behaviour, prints a pass/fail summary, exits non-zero on failure, and deletes everything it made.

Row Behaviour asserted
S20 / S21 name trimmed on create and on update
S44 interior whitespace preserved — trimming is not normalising
S31 unicode and emoji round-trip unchanged
S22 / S24 / S25 whitespace-only, tab/newline-only and empty names rejected with 400
S26 / S27 255 chars accepted, 256 rejected
S28 255 chars wrapped in whitespace accepted — the limit applies after the trim
S2 a draft can be renamed
S23a / S23b a rejected rename returns 400 and leaves the stored name untouched
S3 / S13 copy named Copy of <source>, built from the source's current name
S30 duplicating a copy stacks the prefix (a renewal does not)
S29 a copy of a 255-char name is truncated to 255, so the insert cannot overflow
S72 a sent quote refuses a rename (opt-in)

docs/QUOTE_RENAME_SDK_TEST_PLAN.md

The 46-row plan the examples implement. Row ids are stable and the example output quotes them directly, so a failure reads as FAIL S29 … and maps straight to a plan row.

The send check is deliberately opt-in

S72 sends a quote, which needs an org whose quote template has sender name + email — without them the API returns 400 SenderEmailRequired for an unrelated reason and the check would pass for the wrong reason. It also dispatches a real signature request, which isn't reversible by deleting the quote afterwards.

So it's gated behind RUN_SEND_CHECKS=1 and reports as SKIP, never as a pass. The README in each folder says to use a disposable org.

Verification — all six executed against a live API

17 passed / 0 failed / 1 skipped in every SDK. Ruby and Java were run in official
containers (ruby:3.2-slim, maven:3.9-eclipse-temurin-17) since this machine has no
interpreter/JDK.

SDK Static check Live run
JavaScript/TS tsc --strict 17/17
Python py_compile 17/17
Go go build 17/17
PHP php -l + phpstan + cs-fixer ✅ 17/17
Ruby ruby -c 17/17
Java javac 17/17

Sample output (identical shape in all six):

PASS S20  createQuote trims whitespace       name="Acme Q3"
PASS S44  interior whitespace preserved      name="Acme  Corp"
PASS S22  whitespace-only rejected           400 "name" is not allowed to be empty
PASS S28  255 + whitespace accepted          length=255   (trim before length check)
PASS S3   copy prefixed                      name="Copy of Acme Q3 — Final"
PASS S30  duplicate stacks                   name="Copy of Copy of Acme Q3 — Final"
PASS S29  copy of a 255-char name capped     length=255 prefix="Copy of CCCC"

Each run left zero residue — companies, contacts and quotes all cleaned up.

Running them found three real bugs

None were visible to a type-checker:

  1. .test TLD rejected. dana@rename-example.test fails the backend's email validator. All six switched to example.com.
  2. The PHP example silently targeted PRODUCTION. QuoteClientConfig defaults baseUrl to api.turbodocx.com and does not read TURBODOCX_BASE_URL, so the env var the README documents was ignored — in an example that creates and deletes real records. Now passed explicitly.
  3. PHP TurboDocxException has no getStatusCode() — it exposes ->statusCode as a readonly property. Every rejection assertion fatal-errored on the first 400.

Bug 2 applies to Java as well (HttpClient defaults to production with no env fallback), so that example now passes baseUrl too. Go is the exception — NewQuoteClient reads TURBODOCX_BASE_URL itself — and carries a comment saying so, since the asymmetry reads like an oversight.

Note on credentials

Every example reads TURBODOCX_API_KEY, TURBODOCX_ORG_ID and TURBODOCX_BASE_URL from the environment. Nothing is hardcoded.

Pre-Review Checklist

  • No internal repo or PR references (public repo)
  • Follows each language's idioms rather than transliterating the TS
  • Examples clean up after themselves
  • Non-zero exit on failure, so they drop into CI
  • Executed against a live API — all six SDKs, 17/17 each
  • Ruby + Java compiled and run (official containers)

🤖 Generated with Claude Code

yacineKahlerras and others added 3 commits August 19, 2026 22:52
TurboQuote's naming behaviour changed underneath methods that already existed in
all six SDKs — no method, signature, or type moved, so a caller sees the change
with no version bump and no compile error. That is exactly the kind of change
that needs a runnable check rather than a changelog line.

Adds `examples/quote-rename/` to every SDK: a small self-contained app that
creates its own company, contact and quotes, asserts each behaviour, prints a
pass/fail summary, exits non-zero on failure, and deletes everything it made.

Covered: trimming on create and update, whitespace-only rejection, the 255-char
limit applying after the trim, `Copy of <source>` naming with its 255-char cap,
and the draft-only rename gate.

The send-dependent check is opt-in behind RUN_SEND_CHECKS=1, since it needs an
org with sender identity configured and dispatches a real signature request. It
reports as skipped rather than passed when it does not run.

Also adds docs/QUOTE_RENAME_SDK_TEST_PLAN.md — the 46-row plan the examples
implement, with row ids the example output quotes directly.

Verified: Go compiles, TypeScript type-checks, PHP lints, Python compiles. Ruby
and Java have no toolchain on this machine; their API usage was checked against
the SDK sources by hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI's third PHP gate (cs-fix) failed on the new example — multi-line call
formatting and `fn ($r)` vs `fn($r)`. Applied php-cs-fixer itself rather than
hand-guessing the rules.

All three PHP gates now pass locally: cs-fix (0 of 198 files to fix), phpstan
(no errors), phpunit (341 tests).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ran the examples against a real API rather than trusting the type-check. Three
things only execution could surface:

1. `dana@rename-example.test` was rejected — the backend's email validator does
   not accept the `.test` TLD. Switched to `example.com` in all six, matching the
   existing examples.

2. The PHP example silently targeted PRODUCTION. `QuoteClientConfig` defaults
   `baseUrl` to api.turbodocx.com and does not read TURBODOCX_BASE_URL, so the
   documented env var was ignored — and this example creates and deletes real
   records. Now passed explicitly.

3. PHP `TurboDocxException` exposes `->statusCode` as a readonly property, not
   `getStatusCode()`. The rejection assertions fatal-errored on the first 400.

The same production-targeting trap applies to Java (HttpClient defaults to
api.turbodocx.com with no env fallback), so that example now passes baseUrl too.
Go is the exception — NewQuoteClient reads TURBODOCX_BASE_URL itself — and now
carries a comment saying so, since the asymmetry looks like an oversight.

Live results against a local stack, 17 passed / 0 failed / 1 skipped each:
JavaScript, Python, Go, PHP. Ruby and Java still unexecuted — no toolchain here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@amitsharma-turbodocx
amitsharma-turbodocx force-pushed the docs/turboquote-rename-examples branch from 5898ab2 to cd07403 Compare August 19, 2026 17:25
@amitsharma-turbodocx
amitsharma-turbodocx merged commit e7f1ac5 into main Aug 20, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants