diff --git a/packages/cli/src/__tests__/README.md b/packages/cli/src/__tests__/README.md index c2ad4e6c..911c72da 100644 --- a/packages/cli/src/__tests__/README.md +++ b/packages/cli/src/__tests__/README.md @@ -73,12 +73,19 @@ bun test src/__tests__/manifest.test.ts ### Cloud-specific - `aws.test.ts` — AWS credential cache, SigV4 signing helpers +- `billing-guidance.test.ts` — `isBillingError`, `handleBillingError`, `showNonBillingError` - `cloud-init.test.ts` — `getPackagesForTier`, `needsNode`, `needsBun`, `NODE_INSTALL_CMD` - `check-entity.test.ts` / `check-entity-messages.test.ts` — Entity validation - `agent-tarball.test.ts` — `tryTarballInstall`: GitHub Release tarball install, fallback, URL validation - `gateway-resilience.test.ts` — `startGateway` systemd unit with auto-restart and cron heartbeat - `do-snapshot.test.ts` — `findSpawnSnapshot`: DigitalOcean snapshot lookup, filtering, error handling -- `ui-utils.test.ts` — `validateServerName`, `validateRegionName`, `validateModelId`, `toKebabCase`, `sanitizeTermValue`, `jsonEscape` +- `ui-utils.test.ts` — `validateServerName`, `validateRegionName`, `toKebabCase`, `sanitizeTermValue`, `jsonEscape` + +### Agent-specific +- `junie-agent.test.ts` — Junie CLI agent configuration validation + +### Shared helpers +- `shared-helpers.test.ts` — `generateEnvConfig`, `hasStatus`, `toObjectArray`, `toRecord` ### OAuth and auth - `oauth-code-validation.test.ts` — `OAUTH_CODE_REGEX` format validation diff --git a/packages/cli/src/__tests__/billing-guidance.test.ts b/packages/cli/src/__tests__/billing-guidance.test.ts index c7fcc107..e157a896 100644 --- a/packages/cli/src/__tests__/billing-guidance.test.ts +++ b/packages/cli/src/__tests__/billing-guidance.test.ts @@ -13,9 +13,7 @@ mock.module("../shared/ui", () => ({ prompt: mockPrompt, })); -const { getBillingUrl, getSetupSteps, handleBillingError, isBillingError, showNonBillingError } = await import( - "../shared/billing-guidance" -); +const { handleBillingError, isBillingError, showNonBillingError } = await import("../shared/billing-guidance"); describe("isBillingError", () => { describe("hetzner", () => { @@ -84,18 +82,6 @@ describe("isBillingError", () => { }); }); - describe("daytona", () => { - it("matches billing/plan errors", () => { - expect(isBillingError("daytona", "quota exceeded")).toBe(true); - expect(isBillingError("daytona", "plan limit reached")).toBe(true); - }); - - it("returns false for non-billing errors", () => { - expect(isBillingError("daytona", "sandbox creation failed")).toBe(false); - expect(isBillingError("daytona", "internal server error")).toBe(false); - }); - }); - describe("unknown cloud", () => { it("returns false for unknown clouds", () => { expect(isBillingError("unknown", "billing error")).toBe(false); @@ -103,32 +89,6 @@ describe("isBillingError", () => { }); }); -describe("getBillingUrl", () => { - it("returns correct URLs for known clouds", () => { - expect(getBillingUrl("hetzner")).toBe("https://console.hetzner.cloud/"); - expect(getBillingUrl("digitalocean")).toBe("https://cloud.digitalocean.com/account/billing"); - expect(getBillingUrl("aws")).toBe("https://lightsail.aws.amazon.com/"); - expect(getBillingUrl("gcp")).toBe("https://console.cloud.google.com/billing"); - expect(getBillingUrl("daytona")).toBe("https://app.daytona.io/dashboard"); - }); - - it("returns undefined for unknown clouds", () => { - expect(getBillingUrl("unknown")).toBeUndefined(); - }); -}); - -describe("getSetupSteps", () => { - it("returns steps for known clouds", () => { - const steps = getSetupSteps("hetzner"); - expect(steps.length).toBeGreaterThan(0); - expect(steps[0]).toContain("Hetzner"); - }); - - it("returns empty array for unknown clouds", () => { - expect(getSetupSteps("unknown")).toEqual([]); - }); -}); - describe("handleBillingError", () => { let stderrSpy: ReturnType; diff --git a/packages/cli/src/__tests__/ui-utils.test.ts b/packages/cli/src/__tests__/ui-utils.test.ts index 71f4bff8..579c9bec 100644 --- a/packages/cli/src/__tests__/ui-utils.test.ts +++ b/packages/cli/src/__tests__/ui-utils.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it } from "bun:test"; -const { validateServerName, validateRegionName, validateModelId, toKebabCase, sanitizeTermValue, jsonEscape } = - await import("../shared/ui.js"); +const { validateServerName, validateRegionName, toKebabCase, sanitizeTermValue, jsonEscape } = await import( + "../shared/ui.js" +); // ── validateServerName ────────────────────────────────────────────── @@ -62,26 +63,6 @@ describe("validateRegionName", () => { }); }); -// ── validateModelId ───────────────────────────────────────────────── - -describe("validateModelId", () => { - it("accepts valid model IDs", () => { - expect(validateModelId("anthropic/claude-3.5-sonnet")).toBe(true); - expect(validateModelId("openai/gpt-4")).toBe(true); - expect(validateModelId("meta-llama/llama-3:70b")).toBe(true); - }); - - it("returns true for empty string", () => { - expect(validateModelId("")).toBe(true); - }); - - it("rejects model IDs with invalid characters", () => { - expect(validateModelId("model name")).toBe(false); - expect(validateModelId("model@id")).toBe(false); - expect(validateModelId("model;id")).toBe(false); - }); -}); - // ── toKebabCase ───────────────────────────────────────────────────── describe("toKebabCase", () => { diff --git a/packages/cli/src/shared/billing-guidance.ts b/packages/cli/src/shared/billing-guidance.ts index d51504e2..a379a2dc 100644 --- a/packages/cli/src/shared/billing-guidance.ts +++ b/packages/cli/src/shared/billing-guidance.ts @@ -9,7 +9,6 @@ const BILLING_URLS: Record = { digitalocean: "https://cloud.digitalocean.com/account/billing", aws: "https://lightsail.aws.amazon.com/", gcp: "https://console.cloud.google.com/billing", - daytona: "https://app.daytona.io/dashboard", }; // ─── Setup steps per cloud ────────────────────────────────────────────────── @@ -39,11 +38,6 @@ const SETUP_STEPS: Record = { "3. Enable the Compute Engine API", "4. Return here and press Enter to retry", ], - daytona: [ - "1. Open the Daytona dashboard", - "2. Complete account setup or upgrade your plan", - "3. Return here and press Enter to retry", - ], }; // ─── Error patterns per cloud ─────────────────────────────────────────────── @@ -76,13 +70,6 @@ const ERROR_PATTERNS: Record = { /project.*has.*no.*billing/i, /account[_ ](?:is[_ ])?(?:suspended|closed)/i, ], - daytona: [ - /billing/i, - /payment/i, - /subscription/i, - /quota[_ ]exceeded/i, - /plan[_ ]limit/i, - ], }; /** Check if an error message matches known billing error patterns for a cloud. */ @@ -94,16 +81,6 @@ export function isBillingError(cloud: string, errorMsg: string): boolean { return patterns.some((p) => p.test(errorMsg)); } -/** Get billing setup URL for a cloud. */ -export function getBillingUrl(cloud: string): string | undefined { - return BILLING_URLS[cloud]; -} - -/** Get setup steps for a cloud. */ -export function getSetupSteps(cloud: string): string[] { - return SETUP_STEPS[cloud] || []; -} - /** * Show billing guidance, open the billing page, and prompt user to retry. * Returns true if user wants to retry, false otherwise. diff --git a/packages/cli/src/shared/ui.ts b/packages/cli/src/shared/ui.ts index 3397814c..10489d16 100644 --- a/packages/cli/src/shared/ui.ts +++ b/packages/cli/src/shared/ui.ts @@ -272,14 +272,6 @@ export function validateRegionName(region: string): boolean { return /^[a-zA-Z0-9_-]{1,63}$/.test(region); } -/** Validate model ID format. */ -export function validateModelId(id: string): boolean { - if (!id) { - return true; - } - return /^[a-zA-Z0-9/_:.-]+$/.test(id); -} - /** Convert display name to kebab-case. */ export function toKebabCase(name: string): string { return name