test: tsgo

This commit is contained in:
John Yeo
2026-02-20 09:52:43 +00:00
parent cb40b6ca29
commit f8091e1b13
20 changed files with 773 additions and 558 deletions

239
.github/workflows/sdk-publish.yml vendored Normal file
View File

@@ -0,0 +1,239 @@
name: SDK Publish
on:
push:
branches:
- main
paths:
- 'packages/autumn-js/**'
- 'packages/openapi/**'
workflow_dispatch:
inputs:
dry-run:
description: 'Dry run (no actual publish)'
required: false
default: 'false'
type: choice
options:
- 'true'
- 'false'
test-package:
description: 'Publish to @useautumn/js-test instead'
required: false
default: 'false'
type: choice
options:
- 'true'
- 'false'
version-bump:
description: 'Version bump type (for manual triggers)'
required: false
default: 'patch'
type: choice
options:
- 'patch'
- 'minor'
- 'major'
env:
PACKAGE_DIR: packages/autumn-js
REAL_PACKAGE_NAME: autumn-js
TEST_PACKAGE_NAME: '@useautumn/js-test'
jobs:
publish:
name: Build and Publish
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.2
- name: Set up Node.js (for npm OIDC)
uses: actions/setup-node@v4
with:
node-version: '22.14.0'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: bun install
- name: Determine version bump type
id: version-type
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# Manual trigger - use input
echo "bump=${{ github.event.inputs.version-bump }}" >> $GITHUB_OUTPUT
echo "Using manual version bump: ${{ github.event.inputs.version-bump }}"
else
# Auto trigger - parse commit message
COMMIT_MSG=$(git log -1 --pretty=%B)
echo "Commit message: $COMMIT_MSG"
if echo "$COMMIT_MSG" | grep -qE "^major:|BREAKING CHANGE"; then
echo "bump=major" >> $GITHUB_OUTPUT
echo "Detected major version bump"
elif echo "$COMMIT_MSG" | grep -qE "^minor:|^feat:"; then
echo "bump=minor" >> $GITHUB_OUTPUT
echo "Detected minor version bump"
elif echo "$COMMIT_MSG" | grep -qE "^fix:|^patch:"; then
echo "bump=patch" >> $GITHUB_OUTPUT
echo "Detected patch version bump"
else
echo "bump=skip" >> $GITHUB_OUTPUT
echo "No version bump pattern detected, skipping publish"
fi
fi
- name: Get current version from tags
if: steps.version-type.outputs.bump != 'skip'
id: current-version
run: |
# Get the latest tag for autumn-js
LATEST_TAG=$(git tag -l "autumn-js-v*" --sort=-v:refname | head -n 1)
if [ -z "$LATEST_TAG" ]; then
# No tags yet, start at 1.0.0
echo "current=1.0.0" >> $GITHUB_OUTPUT
echo "No existing tags found, starting at 1.0.0"
else
# Extract version from tag (autumn-js-v1.2.3 -> 1.2.3)
VERSION=${LATEST_TAG#autumn-js-v}
echo "current=$VERSION" >> $GITHUB_OUTPUT
echo "Current version from tag: $VERSION"
fi
- name: Calculate next version
if: steps.version-type.outputs.bump != 'skip'
id: next-version
run: |
CURRENT="${{ steps.current-version.outputs.current }}"
BUMP="${{ steps.version-type.outputs.bump }}"
# Parse semver
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
case "$BUMP" in
major)
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
;;
minor)
MINOR=$((MINOR + 1))
PATCH=0
;;
patch)
PATCH=$((PATCH + 1))
;;
esac
NEXT_VERSION="${MAJOR}.${MINOR}.${PATCH}"
echo "version=$NEXT_VERSION" >> $GITHUB_OUTPUT
echo "Next version: $NEXT_VERSION"
- name: Determine package name
if: steps.version-type.outputs.bump != 'skip'
id: package
run: |
if [ "${{ github.event.inputs.test-package }}" = "true" ]; then
echo "name=${{ env.TEST_PACKAGE_NAME }}" >> $GITHUB_OUTPUT
echo "Publishing to test package: ${{ env.TEST_PACKAGE_NAME }}"
else
echo "name=${{ env.REAL_PACKAGE_NAME }}" >> $GITHUB_OUTPUT
echo "Publishing to real package: ${{ env.REAL_PACKAGE_NAME }}"
fi
- name: Update package.json version and name
if: steps.version-type.outputs.bump != 'skip'
run: |
cd ${{ env.PACKAGE_DIR }}
# Update version
jq --arg version "${{ steps.next-version.outputs.version }}" \
'.version = $version' package.json > package.json.tmp
mv package.json.tmp package.json
# Update package name if using test package
if [ "${{ github.event.inputs.test-package }}" = "true" ]; then
jq --arg name "${{ env.TEST_PACKAGE_NAME }}" \
'.name = $name' package.json > package.json.tmp
mv package.json.tmp package.json
fi
echo "Updated package.json:"
cat package.json | head -20
- name: Regenerate SDK from OpenAPI
if: steps.version-type.outputs.bump != 'skip'
env:
SPEAKEASY_API_KEY: ${{ secrets.SPEAKEASY_API_KEY }}
run: bun api
- name: Build SDK and autumn-js
if: steps.version-type.outputs.bump != 'skip'
run: bun js:build
- name: TypeScript check
if: steps.version-type.outputs.bump != 'skip'
run: bun js:ts
- name: Publish (dry-run)
if: steps.version-type.outputs.bump != 'skip' && github.event.inputs.dry-run == 'true'
run: |
cd ${{ env.PACKAGE_DIR }}
npm publish --dry-run --provenance --access public
echo "✅ Dry run completed successfully"
- name: Publish to npm
if: steps.version-type.outputs.bump != 'skip' && github.event.inputs.dry-run != 'true'
run: |
cd ${{ env.PACKAGE_DIR }}
npm publish --provenance --access public
echo "✅ Published ${{ steps.package.outputs.name }}@${{ steps.next-version.outputs.version }}"
- name: Create git tag
if: steps.version-type.outputs.bump != 'skip' && github.event.inputs.dry-run != 'true' && github.event.inputs.test-package != 'true'
run: |
TAG_NAME="autumn-js-v${{ steps.next-version.outputs.version }}"
git tag "$TAG_NAME"
git push origin "$TAG_NAME"
echo "✅ Created and pushed tag: $TAG_NAME"
- name: Summary
if: steps.version-type.outputs.bump != 'skip'
run: |
echo "## SDK Publish Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Package | ${{ steps.package.outputs.name }} |" >> $GITHUB_STEP_SUMMARY
echo "| Version | ${{ steps.next-version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| Bump Type | ${{ steps.version-type.outputs.bump }} |" >> $GITHUB_STEP_SUMMARY
echo "| Dry Run | ${{ github.event.inputs.dry-run || 'false' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Trigger | ${{ github.event_name }} |" >> $GITHUB_STEP_SUMMARY
- name: Skip notice
if: steps.version-type.outputs.bump == 'skip'
run: |
echo "## SDK Publish Skipped" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "No version bump pattern detected in commit message." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "To trigger a publish, use one of these commit message prefixes:" >> $GITHUB_STEP_SUMMARY
echo "- \`fix:\` or \`patch:\` for patch version" >> $GITHUB_STEP_SUMMARY
echo "- \`feat:\` or \`minor:\` for minor version" >> $GITHUB_STEP_SUMMARY
echo "- \`major:\` or \`BREAKING CHANGE\` for major version" >> $GITHUB_STEP_SUMMARY

View File

@@ -31,7 +31,7 @@
"files.exclude": {
// "**/.claude": true,
"**/.cursor": true,
"**/.github": true,
// "**/.github": true,
"**/.superset": true
}
}

828
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,9 @@ COPY shared/package.json ./shared/package.json
COPY vite/package.json ./vite/package.json
COPY scripts/package.json ./scripts/package.json
COPY apps/checkout/package.json ./apps/checkout/package.json
COPY packages/autumn-js/package.json ./packages/autumn-js/package.json
COPY packages/openapi/package.json ./packages/openapi/package.json
COPY packages/sdk/package.json ./packages/sdk/package.json
# Install dependencies
RUN bun install --ignore-scripts

View File

@@ -18,7 +18,7 @@
"stripe": "19.3.0-beta.1",
"drizzle-orm": "0.43.1",
"drizzle-kit": "^0.31.1",
"tsgo": "npm:@typescript/native-preview@7.0.0-dev.20260218.1",
"@typescript/native-preview": "7.0.0-dev.20260220.1",
"@sentry/bun": "10.38.0",
"@clickhouse/client": "1.11.2",
"@date-fns/utc": "2.1.0",

View File

@@ -1,7 +1,7 @@
{
"name": "autumn-js",
"description": "Autumn JS Library",
"version": "1.0.0-beta.1",
"version": "0.0.1",
"repository": "github:useautumn/autumn-js",
"homepage": "https://docs.useautumn.com",
"main": "./dist/sdk/index.js",
@@ -13,7 +13,7 @@
"LICENSE.md"
],
"scripts": {
"ts": "bunx tsgo --noEmit --skipLibCheck",
"ts": "tsgo --noEmit --skipLibCheck",
"build": "rm -rf dist && tsup",
"prepublishOnly": "bun run build"
},
@@ -75,6 +75,9 @@
],
"author": "John Yeo",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"dependencies": {
"query-string": "^9.2.2",
"rou3": "^0.6.1",
@@ -90,8 +93,8 @@
"next": "^15.2.3",
"react-dom": "^19.1.0",
"tsup": "^8.4.0",
"tsgo": "catalog:",
"typescript": "^5.8.3"
"typescript": "^5.8.3",
"@typescript/native-preview": "catalog:"
},
"peerDependencies": {
"@tanstack/react-query": "^5.0.0",

View File

@@ -20,6 +20,6 @@
"devDependencies": {
"@types/bun": "latest",
"@types/node": "^24.0.3",
"tsgo": "catalog:"
"@typescript/native-preview": "catalog:"
}
}

View File

@@ -26,7 +26,7 @@ generation:
schemas:
allOfMergeStrategy: shallowMerge
requestBodyFieldName: body
versioningStrategy: automatic
versioningStrategy: none
persistentEdits: {}
tests:
generateTests: false

View File

@@ -4,14 +4,14 @@ source "$(dirname "$0")/config.sh"
export TEST_FILE_CONCURRENCY=2
BUN_PARALLEL_V2 \
'integration/billing/update-subscription' \
'integration/billing/migrations' \
'integration/crud/customers' \
'integration/billing/stripe-webhooks' \
'integration/billing/autumn-webhooks' \
'integration/cron' \
'integration/crud/plans' \
# 'integration/billing/update-subscription' \
# 'integration/billing/attach' \
# 'integration/billing/migrations' \
# 'integration/crud/customers' \
# 'integration/billing/stripe-webhooks' \
# 'integration/billing/autumn-webhooks' \
# 'integration/cron' \
# 'integration/crud/plans' \
# 'integration/billing/attach' \

View File

@@ -72,7 +72,13 @@ interface IndividualTest {
interface TestFileResult {
file: string;
status: "pending" | "running" | "passed" | "failed" | "retrying";
status:
| "pending"
| "running"
| "passed"
| "failed"
| "retry_queued"
| "retrying";
tests: IndividualTest[];
currentTest?: string;
duration: number;
@@ -451,9 +457,10 @@ function RunningFile({ result }: RunningFileProps) {
interface RetryingFileProps {
result: TestFileResult;
queuedCount: number;
}
function RetryingFile({ result }: RetryingFileProps) {
function RetryingFile({ result, queuedCount }: RetryingFileProps) {
const fileName = basename(result.file);
return (
<Box>
@@ -461,6 +468,7 @@ function RetryingFile({ result }: RetryingFileProps) {
<Spinner />
<Text color="yellow"> {fileName} </Text>
<Text color="yellow">(retrying...)</Text>
{queuedCount > 0 && <Text dimColor> ({queuedCount} more queued)</Text>}
</Box>
);
}
@@ -564,31 +572,33 @@ function TestRunnerApp({ testFiles, maxParallel }: TestRunnerAppProps) {
);
if (failedFiles.length > 0) {
// Mark files as retrying
// Mark all failed files as queued for retry
for (const result of failedFiles) {
const queuedResult: TestFileResult = {
...result,
status: "retry_queued",
};
pendingRef.current.set(result.file, queuedResult);
}
dirtyRef.current = true;
// Run retries sequentially to avoid resource contention
for (const result of failedFiles) {
// Mark this specific file as actively retrying
const retryingResult: TestFileResult = {
...result,
status: "retrying",
};
pendingRef.current.set(result.file, retryingResult);
}
dirtyRef.current = true;
dirtyRef.current = true;
// Run retries sequentially to avoid resource contention
const retryLimit = pLimit(1);
const retryPromises = failedFiles.map((result) =>
retryLimit(async () => {
const retryResult = await runTestFile(result.file, updateResult, 2);
// If passed on retry, mark it
if (retryResult.status === "passed") {
retryResult.passedOnRetry = true;
pendingRef.current.set(result.file, retryResult);
dirtyRef.current = true;
}
return retryResult;
}),
);
await Promise.all(retryPromises);
const retryResult = await runTestFile(result.file, updateResult, 2);
if (retryResult.status === "passed") {
retryResult.passedOnRetry = true;
pendingRef.current.set(result.file, retryResult);
dirtyRef.current = true;
}
}
}
// Mark retry phase as complete so failed files can be emitted to static
@@ -639,6 +649,9 @@ function TestRunnerApp({ testFiles, maxParallel }: TestRunnerAppProps) {
const allResults = Array.from(results.values());
const runningFiles = allResults.filter((r) => r.status === "running");
const retryingFiles = allResults.filter((r) => r.status === "retrying");
const retryQueuedFiles = allResults.filter(
(r) => r.status === "retry_queued",
);
const completedFiles = allResults.filter(
(r) => r.status === "passed" || r.status === "failed",
@@ -675,11 +688,15 @@ function TestRunnerApp({ testFiles, maxParallel }: TestRunnerAppProps) {
</Box>
)}
{/* Retrying files */}
{/* Retrying file (only 1 at a time, show queued count) */}
{retryingFiles.length > 0 && (
<Box flexDirection="column">
{retryingFiles.map((r) => (
<RetryingFile key={r.file} result={r} />
<RetryingFile
key={r.file}
result={r}
queuedCount={retryQueuedFiles.length}
/>
))}
</Box>
)}
@@ -701,7 +718,10 @@ function TestRunnerApp({ testFiles, maxParallel }: TestRunnerAppProps) {
<Text dimColor> | {runningFiles.length} running</Text>
)}
{retryingFiles.length > 0 && (
<Text color="yellow"> | {retryingFiles.length} retrying</Text>
<Text color="yellow"> | 1 retrying</Text>
)}
{retryQueuedFiles.length > 0 && (
<Text dimColor> | {retryQueuedFiles.length} queued</Text>
)}
</Text>
</Box>

View File

@@ -28,6 +28,7 @@ cusRouter.post("/clear_cache", ...handleClearCustomerCache);
cusRouter.get("/:customer_id", ...handleGetCustomerV2);
cusRouter.post("/:customer_id", ...handleUpdateCustomer);
cusRouter.patch("/:customer_id", ...handleUpdateCustomer);
cusRouter.delete("/:customer_id", ...handleDeleteCustomer);

View File

@@ -5,7 +5,7 @@ import {
ApiVersion,
BillingInterval,
BillingMethod,
type CreatePlanParamsInput,
type CreatePlanParamsV2Input,
ProductItemInterval,
ResetInterval,
TierInfinite,
@@ -28,8 +28,8 @@ test.concurrent(`${chalk.yellowBright("rpc create: metered feature with monthly
await autumnRpc.plans.delete(productId, { allVersions: true });
} catch (_error) {}
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsInput>({
id: productId,
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsV2Input>({
plan_id: productId,
name: "RPC Metered Monthly",
group,
auto_enable: false,
@@ -57,8 +57,8 @@ test.concurrent(`${chalk.yellowBright("rpc create: tiered usage pricing")}`, asy
await autumnRpc.plans.delete(productId, { allVersions: true });
} catch (_error) {}
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsInput>({
id: productId,
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsV2Input>({
plan_id: productId,
name: "RPC Tiered Pricing",
group,
auto_enable: false,
@@ -96,8 +96,8 @@ test.concurrent(`${chalk.yellowBright("rpc create: validation rejects reset/pric
let err: { code?: string } | null = null;
try {
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsInput>({
id: productId,
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsV2Input>({
plan_id: productId,
name: "RPC Invalid Intervals",
group,
auto_enable: false,

View File

@@ -5,12 +5,12 @@ import {
type ApiProduct,
ApiVersion,
BillingInterval,
type CreatePlanParamsInput,
type CreatePlanParamsV2Input,
} from "@autumn/shared";
import { TestFeature } from "@tests/setup/v2Features";
import chalk from "chalk";
import { AutumnRpcCli } from "@/external/autumn/autumnRpcCli.js";
import { AutumnInt } from "@/external/autumn/autumnCli.js";
import { AutumnRpcCli } from "@/external/autumn/autumnRpcCli.js";
const autumnRpc = new AutumnRpcCli({ version: ApiVersion.V2_1 });
const autumnV1_2 = new AutumnInt({ version: ApiVersion.V1_2 });
@@ -24,8 +24,11 @@ test.concurrent(`${chalk.yellowBright("rpc create: minimal plan (id + name only)
await autumnRpc.plans.delete(productId, { allVersions: true });
} catch (_error) {}
const created = await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsInput>({
id: productId,
const created = await autumnRpc.plans.create<
ApiPlanV1,
CreatePlanParamsV2Input
>({
plan_id: productId,
name: "RPC Minimal Plan",
group,
auto_enable: false,
@@ -47,8 +50,11 @@ test.concurrent(`${chalk.yellowBright("rpc create: with base price and flags")}`
await autumnRpc.plans.delete(productId, { allVersions: true });
} catch (_error) {}
const created = await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsInput>({
id: productId,
const created = await autumnRpc.plans.create<
ApiPlanV1,
CreatePlanParamsV2Input
>({
plan_id: productId,
name: "RPC Flags Plan",
group,
add_on: true,
@@ -76,8 +82,11 @@ test.concurrent(`${chalk.yellowBright("rpc create: boolean feature")}`, async ()
await autumnRpc.plans.delete(productId, { allVersions: true });
} catch (_error) {}
const created = await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsInput>({
id: productId,
const created = await autumnRpc.plans.create<
ApiPlanV1,
CreatePlanParamsV2Input
>({
plan_id: productId,
name: "RPC Boolean Plan",
group,
auto_enable: false,
@@ -86,7 +95,7 @@ test.concurrent(`${chalk.yellowBright("rpc create: boolean feature")}`, async ()
expect(created.items.length).toBeGreaterThanOrEqual(1);
const booleanItem = created.items.find(
(item: any) => item.feature_id === TestFeature.Dashboard,
(item) => item.feature_id === TestFeature.Dashboard,
);
expect(booleanItem).toBeDefined();

View File

@@ -29,13 +29,13 @@ test.concurrent(`${chalk.yellowBright("rpc get: get plan response in latest form
ApiPlanV1Schema.parse(plan);
const messagesResponseItem = plan.items.find(
(item: any) => item.feature_id === TestFeature.Messages,
(item) => item.feature_id === TestFeature.Messages,
);
const wordsResponseItem = plan.items.find(
(item: any) => item.feature_id === TestFeature.Words,
(item) => item.feature_id === TestFeature.Words,
);
const creditsResponseItem = plan.items.find(
(item: any) => item.feature_id === TestFeature.Credits,
(item) => item.feature_id === TestFeature.Credits,
);
expect(messagesResponseItem).toBeDefined();

View File

@@ -7,7 +7,7 @@ import {
type ApiProduct,
ApiProductSchema,
ApiVersion,
type CreatePlanParamsInput,
type CreatePlanParamsV2Input,
} from "@autumn/shared";
import { TestFeature } from "@tests/setup/v2Features.js";
import chalk from "chalk";
@@ -28,12 +28,10 @@ test.concurrent(`${chalk.yellowBright("rpc regression: rest list stays cross-ver
try {
await autumnRpc.plans.delete(freeId, { allVersions: true });
} catch (_error) {
// no-op
}
} catch (_error) {}
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsInput>({
id: freeId,
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsV2Input>({
plan_id: freeId,
name: "RPC List Free",
group: freeGroup,
items: [{ feature_id: TestFeature.Credits, included: 500 }],

View File

@@ -0,0 +1,49 @@
import { expect, test } from "bun:test";
import {
type ApiPlanV1,
ApiVersion,
BillingInterval,
type CreatePlanParamsV2Input,
} from "@autumn/shared";
import chalk from "chalk";
import { AutumnRpcCli } from "@/external/autumn/autumnRpcCli.js";
const autumnRpc = new AutumnRpcCli({ version: ApiVersion.V2_1 });
const getSuffix = () => Math.random().toString(36).slice(2, 9);
test.concurrent(`${chalk.yellowBright("rpc delete: create then delete plan successfully")}`, async () => {
const productId = `rpc_delete_${getSuffix()}`;
const group = `rpc_group_${productId}`;
try {
await autumnRpc.plans.delete(productId, { allVersions: true });
} catch (_error) {}
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsV2Input>({
plan_id: productId,
name: "RPC Delete Test",
group,
auto_enable: false,
price: {
amount: 1900,
interval: BillingInterval.Month,
},
});
const beforeDelete = await autumnRpc.plans.get<ApiPlanV1>(productId);
expect(beforeDelete.id).toBe(productId);
const deleteResult = await autumnRpc.plans.delete(productId, {
allVersions: false,
});
expect(deleteResult.success).toBe(true);
let err: unknown = null;
try {
await autumnRpc.plans.get(productId);
} catch (error) {
err = error;
}
expect(err).toBeDefined();
});

View File

@@ -1,10 +1,16 @@
import { expect, test } from "bun:test";
import { ApiVersion, FreeTrialDuration, ResetInterval } from "@autumn/shared";
import {
ApiVersion,
type CreatePlanParamsV2Input,
FreeTrialDuration,
ResetInterval,
type UpdatePlanParamsV2Input,
} from "@autumn/shared";
import { TestFeature } from "@tests/setup/v2Features";
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
import chalk from "chalk";
import { AutumnRpcCli } from "@/external/autumn/autumnRpcCli.js";
import { AutumnInt } from "@/external/autumn/autumnCli.js";
import { AutumnRpcCli } from "@/external/autumn/autumnRpcCli.js";
import { ProductService } from "@/internal/products/ProductService.js";
const autumnV2 = new AutumnInt({ version: ApiVersion.V2_0 });
@@ -13,6 +19,8 @@ const autumnRpc = new AutumnRpcCli({ version: ApiVersion.V2_1 });
const { db, org, env } = ctx;
const getSuffix = () => Math.random().toString(36).slice(2, 9);
type UpdatePlanRpcInput = Omit<UpdatePlanParamsV2Input, "plan_id">;
test.concurrent(`${chalk.yellowBright("rpc regression: rest update then rpc inverse keeps product stable")}`, async () => {
const productId = `rpc_roundtrip_${getSuffix()}`;
const baselineGroup = `rpc_regression_baseline_${productId}`;
@@ -59,13 +67,16 @@ test.concurrent(`${chalk.yellowBright("rpc regression: rest update then rpc inve
await autumnRpc.plans.delete(productId, { allVersions: true });
} catch (_error) {}
await autumnRpc.plans.create({
id: productId,
await autumnRpc.plans.create<unknown, CreatePlanParamsV2Input>({
plan_id: productId,
...baseline,
});
await autumnV2.products.update(productId, restUpdates);
await autumnRpc.plans.update(productId, baseline);
await autumnRpc.plans.update<unknown, UpdatePlanRpcInput>(
productId,
baseline,
);
const finalFull = await ProductService.getFull({
db,
@@ -78,7 +89,8 @@ test.concurrent(`${chalk.yellowBright("rpc regression: rest update then rpc inve
expect(finalFull.group).toBe(baseline.group);
expect(finalFull.is_add_on).toBe(baseline.add_on);
expect(
finalFull.entitlements.find((ent) => ent.feature_id === TestFeature.Messages)
?.allowance,
finalFull.entitlements.find(
(ent) => ent.feature_id === TestFeature.Messages,
)?.allowance,
).toBe(100);
});

View File

@@ -1,5 +1,10 @@
import { expect, test } from "bun:test";
import { ApiVersion, BillingInterval, type CreatePlanParamsInput } from "@autumn/shared";
import {
type ApiPlanV1,
ApiVersion,
BillingInterval,
type CreatePlanParamsV2Input,
} from "@autumn/shared";
import chalk from "chalk";
import { AutumnRpcCli } from "@/external/autumn/autumnRpcCli.js";
@@ -14,8 +19,8 @@ test.concurrent(`${chalk.yellowBright("rpc delete: create then delete plan succe
await autumnRpc.plans.delete(productId, { allVersions: true });
} catch (_error) {}
await autumnRpc.plans.create<any, CreatePlanParamsInput>({
id: productId,
await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsV2Input>({
plan_id: productId,
name: "RPC Delete Test",
group,
auto_enable: false,
@@ -25,7 +30,7 @@ test.concurrent(`${chalk.yellowBright("rpc delete: create then delete plan succe
},
});
const beforeDelete = await autumnRpc.plans.get<any>(productId);
const beforeDelete = await autumnRpc.plans.get<ApiPlanV1>(productId);
expect(beforeDelete.id).toBe(productId);
const deleteResult = await autumnRpc.plans.delete(productId, {
@@ -33,7 +38,7 @@ test.concurrent(`${chalk.yellowBright("rpc delete: create then delete plan succe
});
expect(deleteResult.success).toBe(true);
let err: any = null;
let err: unknown = null;
try {
await autumnRpc.plans.get(productId);
} catch (error) {

View File

@@ -3,6 +3,7 @@ import {
type ApiPlanV1,
ApiVersion,
BillingInterval,
type CreatePlanParamsV2Input,
ResetInterval,
type UpdatePlanParamsV2Input,
} from "@autumn/shared";
@@ -44,7 +45,7 @@ const createTestPlan = async (planId: string) => {
await autumnRpc.plans.delete(planId, { allVersions: true });
} catch (_error) {}
return await autumnRpc.plans.create<ApiPlanV1>({
return await autumnRpc.plans.create<ApiPlanV1, CreatePlanParamsV2Input>({
plan_id: planId,
name: `Test Plan ${planId}`,
group: `group_${planId}`,

View File

@@ -6,7 +6,7 @@ import {
type CreateProductV2ParamsInput,
ProductItemInterval,
ResetInterval,
type UpdatePlanParamsInput,
type UpdatePlanParamsV2Input,
} from "@autumn/shared";
import { TestFeature } from "@tests/setup/v2Features";
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
@@ -19,6 +19,7 @@ const autumnRpc = new AutumnRpcCli({ version: ApiVersion.V2_1 });
const autumnV1_2 = new AutumnInt({ version: ApiVersion.V1_2 });
const { db, org, env } = ctx;
type UpdatePlanRpcInput = Omit<UpdatePlanParamsV2Input, "plan_id">;
test.concurrent(`${chalk.yellowBright("rpc update: match existing entitlement by feature_id (no entitlement_id)")}`, async () => {
const productId = "rpc_update_match_1";
@@ -52,7 +53,7 @@ test.concurrent(`${chalk.yellowBright("rpc update: match existing entitlement by
?.id,
).toBeDefined();
await autumnRpc.plans.update<ApiPlanV1, UpdatePlanParamsInput>(productId, {
await autumnRpc.plans.update<ApiPlanV1, UpdatePlanRpcInput>(productId, {
items: [
{
feature_id: TestFeature.Messages,
@@ -96,7 +97,7 @@ test.concurrent(`${chalk.yellowBright("rpc update: match entitlement with same f
],
});
await autumnRpc.plans.update<ApiPlanV1, UpdatePlanParamsInput>(productId, {
await autumnRpc.plans.update<ApiPlanV1, UpdatePlanRpcInput>(productId, {
items: [
{
feature_id: TestFeature.Messages,
@@ -140,7 +141,7 @@ test.concurrent(`${chalk.yellowBright("rpc update: create NEW entitlement when i
],
});
await autumnRpc.plans.update<ApiPlanV1, UpdatePlanParamsInput>(productId, {
await autumnRpc.plans.update<ApiPlanV1, UpdatePlanRpcInput>(productId, {
items: [
{
feature_id: TestFeature.Messages,
@@ -189,7 +190,7 @@ test.concurrent(`${chalk.yellowBright("rpc update: handle multiple features with
],
});
await autumnRpc.plans.update<ApiPlanV1, UpdatePlanParamsInput>(productId, {
await autumnRpc.plans.update<ApiPlanV1, UpdatePlanRpcInput>(productId, {
items: [
{
feature_id: TestFeature.Messages,