refactor(onboarding): remove legacy start-trial flow and endpoint - #3569
refactor(onboarding): remove legacy start-trial flow and endpoint#3569ygrishajev wants to merge 1 commit into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
💤 Files with no reviewable changes (4)
📝 WalkthroughWalkthroughThe change removes the legacy trial-start API and managed-wallet creation flow. It deletes the related onboarding UI, updates wallet state to use retrieval only, and changes verification and wallet connection navigation. ChangesLegacy trial API removal
Managed-wallet retrieval state
Onboarding removal and navigation
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
❌ 2 Tests Failed:
View the top 1 failed test(s) by shortest run time
View the full list of 1 ❄️ flaky test(s)
To view more test analytics, go to the Test Analytics Dashboard |
558ca82 to
c7c895d
Compare
Trial wallet provisioning now runs server-side (the wallet is ensured at registration and activation is dispatched off registration/email-verification) and the UI polls the wallet until it is ready, so the client-triggered start-trial path is redundant. Remove the POST /v1/start-trial endpoint together with its controller shim, request/response schemas, and the create-UserWallet ability, and remove the client that called it (ManagedWalletHttpService.createWallet, useCreateManagedWalletMutation), slimming WalletProvider and useManagedWallet to reporting-only. Remove the superseded /signup onboarding wizard and its steps; the onboarding entry is now the /onboarding picker gated by RequireOnboarding. Keep VerificationCodeInput (shared with passwordless auth) and repoint /user/verify-email to home.
c7c895d to
6ffcae5
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/deploy-web/src/components/wallet/ConnectManagedWalletButton.tsx (1)
29-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a click-navigation assertion.
ConnectManagedWalletButton.spec.tsxtests only enabled and disabled states. Add a test that verifiesrouter.pushreceivesUrlService.onboardingPicker(). This protects the changed destination from regression.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/deploy-web/src/components/wallet/ConnectManagedWalletButton.tsx` around lines 29 - 31, Extend ConnectManagedWalletButton.spec.tsx with an enabled-button click test that invokes the handler and asserts router.push receives UrlService.onboardingPicker(). Reuse the existing router mock and button setup while preserving the current enabled and disabled state tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/api/src/billing/controllers/wallet/wallet.controller.spec.ts`:
- Around line 64-68: Update the AuthService mock setup around the rootContainer
registration: create the mock without the inline ability override, then assign
the real createMongoAbility result to its ability property afterward so CASL
internals are not recursively mocked. Ensure authorization assertions use a
throwUnlessCan implementation that delegates to the assigned ability when
needed.
---
Nitpick comments:
In `@apps/deploy-web/src/components/wallet/ConnectManagedWalletButton.tsx`:
- Around line 29-31: Extend ConnectManagedWalletButton.spec.tsx with an
enabled-button click test that invokes the handler and asserts router.push
receives UrlService.onboardingPicker(). Reuse the existing router mock and
button setup while preserving the current enabled and disabled state tests.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 167fa368-db2a-4ca7-ac3d-870ac472a31e
📒 Files selected for processing (55)
apps/api/src/auth/services/ability/ability.service.tsapps/api/src/billing/controllers/wallet/wallet.controller.spec.tsapps/api/src/billing/controllers/wallet/wallet.controller.tsapps/api/src/billing/http-schemas/wallet.schema.tsapps/api/src/billing/routes/index.tsapps/api/src/billing/routes/start-trial/start-trial.router.tsapps/api/src/routers/open-api-handlers.tsapps/api/swagger/openapi.jsonapps/deploy-web/src/components/deployments/DeploymentDetailTopBar/DeploymentDetailTopBar.spec.tsxapps/deploy-web/src/components/deployments/ManifestUpdate/ManifestUpdate.spec.tsxapps/deploy-web/src/components/home/YourAccount/YourAccount.spec.tsxapps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsxapps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsxapps/deploy-web/src/components/onboarding/OnboardingPage.tsxapps/deploy-web/src/components/onboarding/OnboardingStepper/OnboardingStepper.tsxapps/deploy-web/src/components/onboarding/OnboardingView/OnboardingView.tsxapps/deploy-web/src/components/onboarding/VerifyEmailPage/VerifyEmailPage.spec.tsxapps/deploy-web/src/components/onboarding/VerifyEmailPage/VerifyEmailPage.tsxapps/deploy-web/src/components/onboarding/steps/EmailVerificationContainer/EmailVerificationContainer.spec.tsxapps/deploy-web/src/components/onboarding/steps/EmailVerificationContainer/EmailVerificationContainer.tsxapps/deploy-web/src/components/onboarding/steps/EmailVerificationStep/EmailVerificationStep.spec.tsxapps/deploy-web/src/components/onboarding/steps/EmailVerificationStep/EmailVerificationStep.tsxapps/deploy-web/src/components/onboarding/steps/FreeTrialLandingStep/FreeTrialLandingStep.tsxapps/deploy-web/src/components/onboarding/steps/PaymentMethodContainer/PaymentMethodContainer.spec.tsxapps/deploy-web/src/components/onboarding/steps/PaymentMethodContainer/PaymentMethodContainer.tsxapps/deploy-web/src/components/onboarding/steps/PaymentMethodStep/PaymentMethodStep.tsxapps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/EmptyPaymentMethods.tsxapps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/ErrorAlert.tsxapps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/PaymentMethodsDisplay.test.tsxapps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/PaymentMethodsDisplay.tsxapps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/TermsAndConditions.tsxapps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/TrialStartButton.tsxapps/deploy-web/src/components/onboarding/steps/PaymentVerificationCard/PaymentVerificationCard.tsxapps/deploy-web/src/components/onboarding/steps/WelcomeStep/TemplateCard.tsxapps/deploy-web/src/components/onboarding/steps/WelcomeStep/TrialStatusBar.tsxapps/deploy-web/src/components/onboarding/steps/WelcomeStep/WelcomeStep.tsxapps/deploy-web/src/components/wallet/ConnectManagedWalletButton.tsxapps/deploy-web/src/context/WalletProvider/WalletProvider.tsxapps/deploy-web/src/hooks/useManagedWallet.spec.tsxapps/deploy-web/src/hooks/useManagedWallet.tsapps/deploy-web/src/hooks/useOnboardingChrome.spec.tsapps/deploy-web/src/hooks/useOnboardingChrome.tsapps/deploy-web/src/hooks/useReturnTo/useReturnTo.tsapps/deploy-web/src/pages/signup/index.tsxapps/deploy-web/src/queries/queryKeys.tsapps/deploy-web/src/queries/useManagedWalletQuery.spec.tsxapps/deploy-web/src/queries/useManagedWalletQuery.tsapps/deploy-web/src/services/analytics/analytics.service.tsapps/deploy-web/src/services/app-di-container/app-di-container.tsapps/deploy-web/src/utils/urlUtils.tsapps/deploy-web/tests/seeders/wallet.tsapps/deploy-web/tests/ui/pages/HomePage.tsapps/deploy-web/tests/ui/pages/OnboardingPage.tspackages/console-api-types/src/schema.d.tspackages/http-sdk/src/managed-wallet-http/managed-wallet-http.service.ts
💤 Files with no reviewable changes (39)
- apps/deploy-web/src/components/home/YourAccount/YourAccount.spec.tsx
- apps/api/src/billing/routes/index.ts
- apps/deploy-web/src/components/onboarding/steps/WelcomeStep/TrialStatusBar.tsx
- apps/deploy-web/src/components/deployments/ManifestUpdate/ManifestUpdate.spec.tsx
- apps/deploy-web/src/components/onboarding/steps/WelcomeStep/TemplateCard.tsx
- apps/deploy-web/src/components/onboarding/OnboardingPage.tsx
- apps/deploy-web/src/pages/signup/index.tsx
- apps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/ErrorAlert.tsx
- apps/deploy-web/tests/seeders/wallet.ts
- apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
- apps/api/swagger/openapi.json
- apps/deploy-web/src/components/onboarding/steps/PaymentMethodContainer/PaymentMethodContainer.spec.tsx
- apps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/TermsAndConditions.tsx
- apps/api/src/routers/open-api-handlers.ts
- packages/http-sdk/src/managed-wallet-http/managed-wallet-http.service.ts
- apps/deploy-web/src/components/onboarding/steps/EmailVerificationContainer/EmailVerificationContainer.tsx
- apps/deploy-web/src/components/onboarding/steps/PaymentMethodStep/PaymentMethodStep.tsx
- apps/deploy-web/src/components/onboarding/steps/WelcomeStep/WelcomeStep.tsx
- apps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/PaymentMethodsDisplay.tsx
- apps/deploy-web/src/components/onboarding/steps/FreeTrialLandingStep/FreeTrialLandingStep.tsx
- apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx
- apps/deploy-web/src/utils/urlUtils.ts
- apps/deploy-web/src/components/onboarding/steps/PaymentMethodContainer/PaymentMethodContainer.tsx
- packages/console-api-types/src/schema.d.ts
- apps/deploy-web/tests/ui/pages/OnboardingPage.ts
- apps/deploy-web/src/components/onboarding/OnboardingStepper/OnboardingStepper.tsx
- apps/deploy-web/src/services/analytics/analytics.service.ts
- apps/deploy-web/src/queries/queryKeys.ts
- apps/deploy-web/src/components/deployments/DeploymentDetailTopBar/DeploymentDetailTopBar.spec.tsx
- apps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/PaymentMethodsDisplay.test.tsx
- apps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/TrialStartButton.tsx
- apps/api/src/billing/routes/start-trial/start-trial.router.ts
- apps/deploy-web/src/components/onboarding/steps/PaymentMethodsDisplay/EmptyPaymentMethods.tsx
- apps/deploy-web/src/components/onboarding/steps/EmailVerificationStep/EmailVerificationStep.spec.tsx
- apps/api/src/billing/http-schemas/wallet.schema.ts
- apps/deploy-web/src/components/onboarding/OnboardingView/OnboardingView.tsx
- apps/deploy-web/src/components/onboarding/steps/EmailVerificationStep/EmailVerificationStep.tsx
- apps/deploy-web/src/components/onboarding/steps/PaymentVerificationCard/PaymentVerificationCard.tsx
- apps/deploy-web/src/components/onboarding/steps/EmailVerificationContainer/EmailVerificationContainer.spec.tsx
| rootContainer.register(AuthService, { | ||
| useValue: mock<AuthService>({ | ||
| ability: createMongoAbility<MongoAbility>([{ action: "create", subject: "UserWallet" }]), | ||
| ability: createMongoAbility<MongoAbility>([{ action: "read", subject: "UserWallet" }]), | ||
| currentUser: input?.user ?? createUser() | ||
| }) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- CLAUDE files ---'
find .. -name CLAUDE.md -print
printf '%s\n' '--- target test file ---'
sed -n '1,150p' apps/api/src/billing/controllers/wallet/wallet.controller.spec.ts
printf '%s\n' '--- mock<AuthService> usage ---'
rg -n -C 3 'mock<AuthService>|createMongoAbility|rootContainer.register\(AuthService' apps/api/src/billing apps/api/src | head -240
printf '%s\n' '--- dependency declarations ---'
rg -n -C 2 'vitest-mock-extended|vitest' package.json apps/api/package.json pnpm-lock.yaml yarn.lock package-lock.json 2>/dev/null | head -160Repository: akash-network/console
Length of output: 36565
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- repository guidance ---'
cat CLAUDE.md
printf '%s\n' '--- AuthService ability declaration and access ---'
rg -n -C 5 'ability|currentUser' apps/api/src/auth/services/auth.service.ts apps/api/src/billing/controllers/wallet/wallet.controller.ts apps/api/src/billing/services/wallet-reader/wallet-reader.service.ts
printf '%s\n' '--- mock library implementation ---'
find node_modules/vitest-mock-extended -maxdepth 3 -type f -print | sort | head -80
rg -n -C 8 'function mock|const mock|mock<' node_modules/vitest-mock-extended/dist node_modules/vitest-mock-extended/lib 2>/dev/null | head -240
printf '%s\n' '--- relevant package metadata ---'
node -e 'const p=require("./node_modules/vitest-mock-extended/package.json"); console.log(JSON.stringify({version:p.version,main:p.main,exports:p.exports},null,2))'Repository: akash-network/console
Length of output: 17353
🌐 Web query:
vitest-mock-extended v4 mock nested object overrides implementation
💡 Result:
In vitest-mock-extended, you can override implementations for nested objects or methods by using the mockDeep<T>() function and providing a partial implementation [1][2]. To override nested properties while maintaining deep mocking capabilities: 1. Pass your partial implementation object directly into the mockDeep<T>() function [1]. 2. The library internally uses an overrideMockImp function that recursively applies these provided implementations to the object structure [1][3]. 3. For properties you do not explicitly define in your override, the deep proxy handler will continue to automatically generate mock implementations (or use a fallbackMockImplementation if provided) when those properties are accessed [1][2][4]. Example pattern: const myMock = mockDeep({ nested: { property: 'overridden-value', method: vi.fn.mockReturnValue('custom-result') } }); In this example, calling myMock.nested.property will return 'overridden-value', while other unmocked properties or nested objects within myMock will remain automatically mocked via the deep proxy system [1][4]. If you need to provide a default behavior for all unmocked methods, you can pass a fallbackMockImplementation in the options object [1][2][4]: const myMock = mockDeep({ // options }, { fallbackMockImplementation: (propName) =>... }); The internal overrideMockImp ensures that your custom defined structure is preserved while the remaining graph is automatically filled with proxies [1][3].
Citations:
- 1: https://deepwiki.com/eratio08/vitest-mock-extended/2.1-mock-creation-system
- 2: https://github.com/eratio08/vitest-mock-extended/blob/c12fe299/README.md
- 3: https://github.com/eratio08/vitest-mock-extended/blob/c12fe299/src/Mock.ts
- 4: https://deepwiki.com/eratio08/vitest-mock-extended/5.2-usage-examples-and-patterns
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- installed dependency locations ---'
find . -path '*/node_modules/vitest-mock-extended/package.json' -print
printf '%s\n' '--- package-lock entries ---'
sed -n '3980,4045p' package-lock.json
rg -n -C 3 '"vitest-mock-extended"' package-lock.json
printf '%s\n' '--- AuthService complete implementation ---'
cat -n apps/api/src/auth/services/auth.service.ts
printf '%s\n' '--- WalletController implementation ---'
cat -n apps/api/src/billing/controllers/wallet/wallet.controller.ts
printf '%s\n' '--- repository test patterns assigning ability ---'
rg -n -C 5 'const .*authService = mock<AuthService>|authService\.ability|mock<AuthService>\(\{[[:space:]]*$' apps/api/src --glob '*.spec.ts' | head -320Repository: akash-network/console
Length of output: 42108
🌐 Web query:
site:github.com/eratio08/vitest-mock-extended/blob v4 src Mock.ts mock overrideMockImp
💡 Result:
In the context of the vitest-mock-extended library, overrideMockImp is an internal utility function located in src/Mock.ts [1]. It is used to recursively construct a proxy-based mock object [1]. The function iterates over the properties of the provided obj and applies a proxy handler [1]. If a property is an object (and not null), overrideMockImp is called recursively to ensure deep mocking [1]. Otherwise, the property value is assigned directly to the proxy [1]. This mechanism is fundamental to how mock() and mockDeep() generate objects that support property access and function spying while maintaining type safety [1][2][3]. While overrideMockImp is not typically intended for direct use by consumers of the library, it supports the library's ability to recursively mock complex structures and handle custom mock implementations [1][4]. Users typically interact with this functionality through the public mock() or mockDeep() APIs [5][2][3].
Citations:
- 1: https://github.com/eratio08/vitest-mock-extended/blob/c12fe299/src/Mock.ts
- 2: https://github.com/eratio08/vitest-mock-extended/blob/c12fe299/README.md
- 3: https://github.com/eratio08/vitest-mock-extended/blob/c12fe299/example/src/mock-example.spec.ts
- 4: https://github.com/eratio08/vitest-mock-extended/blob/c12fe299/src/Mock.spec.ts
- 5: https://github.com/eratio08/vitest-mock-extended/blob/main/README.md
🏁 Script executed:
#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://raw.githubusercontent.com/eratio08/vitest-mock-extended/c12fe299/src/Mock.ts' -o "$tmpdir/Mock.ts"
curl -fsSL 'https://raw.githubusercontent.com/eratio08/vitest-mock-extended/c12fe299/src/Mock.spec.ts' -o "$tmpdir/Mock.spec.ts"
printf '%s\n' '--- mock implementation ---'
cat "$tmpdir/Mock.ts"
printf '%s\n' '--- nested override tests ---'
rg -n -C 8 'override|nested|mockDeep|mock<' "$tmpdir/Mock.spec.ts" | head -320Repository: akash-network/console
Length of output: 19403
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- CASL dependency locations ---'
find . -path '*/node_modules/@casl/ability/package.json' -print
printf '%s\n' '--- CASL versions in lockfile ---'
rg -n -C 4 '"`@casl/ability`"|casl-ability' package-lock.json apps/api/package.json
printf '%s\n' '--- npm cache entries for relevant packages ---'
find ~/.npm -type f \( -iname '*casl*' -o -iname '*vitest-mock-extended*' \) -print 2>/dev/null | head -80
printf '%s\n' '--- source references to ability behavior ---'
rg -n -C 5 'ability\.(can|rules|update|cannot)|accessibleBy\(.*ability' apps/api/src --glob '*.spec.ts' --glob '*.ts' | head -320Repository: akash-network/console
Length of output: 35400
🌐 Web query:
CASL ability v6 createMongoAbility AbilityBuilder source rules own properties can method
💡 Result:
In CASL v6, the AbilityBuilder is a utility used to declaratively define authorization rules [1][2]. It works by wrapping a factory function, such as createMongoAbility, to produce an ability instance [1][3]. To define rules, you use the can and cannot methods provided by the builder [1][3]. When defining rules for objects that belong to a specific user (e.g., "own" properties), you typically pass a conditions object as the third argument to the can method [3]. Example of defining permissions for "own" resources: import { AbilityBuilder, createMongoAbility } from '@casl/ability'; function defineAbilitiesFor(user) { const { can, build } = new AbilityBuilder(createMongoAbility); // Allow managing posts where the author's id matches the user's id can('manage', 'BlogPost', { author: user.id }); return build; } In this pattern: 1. createMongoAbility: Used to initialize the ability instance with MongoDB-style condition matching [4][3]. 2. AbilityBuilder: Provides the can and cannot methods [1][3]. 3. Conditions: When you pass an object like { author: user.id }, CASL interprets this as a condition to be matched against the subject when you later call ability.can('manage', subject) [3]. The library checks if the property of the subject (e.g., subject.author) matches the value provided in your rule (e.g., user.id) [3].
Citations:
- 1: https://github.com/stalniy/casl/blob/a94cdcbb/packages/casl-ability/src/AbilityBuilder.ts
- 2: Use AbilityBuilder or createMongoAbility?? stalniy/casl#898
- 3: https://github.com/stalniy/casl/blob/a94cdcbb/packages/casl-ability/README.md
- 4: https://github.com/stalniy/casl/blob/a94cdcbb/packages/casl-ability/src/Ability.ts
🏁 Script executed:
#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
for file in Ability.ts PureAbility.ts RuleIndex.ts; do
curl -fsSL "https://raw.githubusercontent.com/stalniy/casl/v6.7.3/packages/casl-ability/src/$file" -o "$tmpdir/$file" || true
done
printf '%s\n' '--- CASL source files retrieved ---'
ls -l "$tmpdir"
printf '%s\n' '--- CASL constructors and methods ---'
rg -n -C 12 'constructor|can\(|rules|actions|subjects|merged|createMongoAbility' "$tmpdir" | head -500Repository: akash-network/console
Length of output: 388
🏁 Script executed:
#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
for file in Ability.ts PureAbility.ts RuleIndex.ts; do
url="https://raw.githubusercontent.com/stalniy/casl/a94cdcbb/packages/casl-ability/src/$file"
if curl -fsSL "$url" -o "$tmpdir/$file"; then
printf 'retrieved %s\n' "$file"
fi
done
printf '%s\n' '--- retrieved CASL source ---'
for file in "$tmpdir"/*; do
printf '\n--- %s ---\n' "$file"
cat "$file"
doneRepository: akash-network/console
Length of output: 13816
🏁 Script executed:
#!/bin/bash
set -eu
node <<'JS'
"use strict";
// Minimal standalone reproduction of vitest-mock-extended's mock()/overrideMockImp
// behavior and CASL's Map-backed ability access. This does not load repository code.
function mockFunction() {
const fn = function () {};
fn._isMockFunction = true;
return fn;
}
const handler = {
ownKeys(target) {
return Reflect.ownKeys(target);
},
set(target, property, value) {
target[property] = value;
return true;
},
get(target, property) {
if (!(property in target)) {
if (property === "_isMockObject" || property === "_isMockFunction") return undefined;
target[property] = mockFunction();
}
return target[property];
}
};
function overrideMockImp(obj) {
const proxy = new Proxy(obj, handler);
for (const name of Object.keys(obj)) {
if (typeof obj[name] === "object" && obj[name] !== null) {
proxy[name] = overrideMockImp(obj[name]);
} else {
proxy[name] = obj[name];
}
}
return proxy;
}
function mock(implementation = {}) {
implementation._isMockObject = true;
return overrideMockImp(implementation);
}
class AbilityLike {
constructor() {
this.index = new Map([["read:UserWallet", true]]);
}
can(action, subject) {
return this.index.get(`${action}:${subject}`) === true;
}
}
const ability = new AbilityLike();
const nested = mock({ ability });
const assigned = mock({});
assigned.ability = ability;
for (const [label, value] of [["nested override", nested.ability], ["post-assignment", assigned.ability]]) {
try {
console.log(label, "=>", value.can("read", "UserWallet"));
} catch (error) {
console.log(label, "=>", `${error.name}: ${error.message}`);
}
}
JSRepository: akash-network/console
Length of output: 347
🏁 Script executed:
#!/bin/bash
set -eu
node <<'JS'
"use strict";
function mockFunction() {
return function () {};
}
const handler = {
ownKeys: target => Reflect.ownKeys(target),
set(target, property, value) {
target[property] = value;
return true;
},
get(target, property) {
if (!(property in target)) {
if (property === "_isMockObject" || property === "_isMockFunction") return undefined;
target[property] = mockFunction();
}
return target[property];
}
};
function overrideMockImp(obj) {
const proxy = new Proxy(obj, handler);
for (const name of Object.keys(obj)) {
proxy[name] =
typeof obj[name] === "object" && obj[name] !== null
? overrideMockImp(obj[name])
: obj[name];
}
return proxy;
}
function mock(implementation = {}) {
implementation._isMockObject = true;
return overrideMockImp(implementation);
}
class AbilityLike {
constructor() {
this.index = new Map([["read:UserWallet", true]]);
}
can(action, subject) {
return this.index.get(`${action}:${subject}`) === true;
}
}
const nestedAbility = new AbilityLike();
const nested = mock({ ability: nestedAbility });
const assigned = mock({});
assigned.ability = new AbilityLike();
for (const [label, value] of [
["nested override", nested.ability],
["post-assignment", assigned.ability]
]) {
try {
console.log(label, "=>", value.can("read", "UserWallet"));
} catch (error) {
console.log(label, "=>", `${error.name}: ${error.message}`);
}
}
JSRepository: akash-network/console
Length of output: 277
Assign ability after creating the AuthService mock.
mock() recursively wraps object-valued overrides. Passing createMongoAbility(...) inline wraps CASL's internal Map, so ability.can(...) can throw a TypeError. Assign the ability after creating the mock. If this test must cover authorization, make throwUnlessCan delegate to that ability because the current auto-mocked method bypasses CASL.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/api/src/billing/controllers/wallet/wallet.controller.spec.ts` around
lines 64 - 68, Update the AuthService mock setup around the rootContainer
registration: create the mock without the inline ability override, then assign
the real createMongoAbility result to its ability property afterward so CASL
internals are not recursively mocked. Ensure authorization assertions use a
throwUnlessCan implementation that delegates to the assigned ability when
needed.
Source: Learnings
| import { validatorsRouter } from "@src/validator"; | ||
|
|
||
| export const openApiHonoHandlers: OpenApiHonoHandler[] = [ | ||
| startTrialRouter, | ||
| getWalletListRouter, | ||
| walletSettingRouter, | ||
| signAndBroadcastTxRouter, |
There was a problem hiding this comment.
🔴 Removing the /v1/start-trial route (via open-api-handlers.ts) changes the generated /v1/doc?scope=console output, but the checked-in functional test snapshot at apps/api/test/functional/snapshots/docs.spec.ts.snap (~line 13379) still contains the old /v1/start-trial path entry. Since that snapshot file wasn't regenerated in this PR, docs.spec.ts's toMatchSnapshot() assertion will fail in CI until it's updated with vitest -u.
Extended reasoning...
apps/api/test/functional/docs.spec.ts issues a GET /v1/doc?scope=console request and asserts the response body against a Jest/Vitest snapshot stored at apps/api/test/functional/__snapshots__/docs.spec.ts.snap via expect(data).toMatchSnapshot(). That endpoint's output is built dynamically from the OpenAPI definitions registered on the openApiHonoHandlers array in apps/api/src/routers/open-api-handlers.ts — it is not sourced from the static swagger/openapi.json file.
This PR removes startTrialRouter from that array (and deletes apps/api/src/billing/routes/start-trial/start-trial.router.ts entirely), so once the app boots with these changes, the OpenAPI document generated for the console scope will no longer contain a /v1/start-trial path. However, the committed snapshot file was not touched by this PR (it does not appear in the changed-files list), and it still contains a full /v1/start-trial path block, including the specific backward-compat description text ("Ensures the user's managed wallet exists and enqueues background trial activation...") that only existed on the now-deleted route.
Concretely, here's how this manifests in CI:
- The functional test suite boots the API and calls
GET /v1/doc?scope=console. - The response is serialized as JSON and compared with
toMatchSnapshot()against the stored.snapfile. - The live response omits
/v1/start-trial(sincestartTrialRouteris no longer registered), while the snapshot still expects it present. - Vitest reports a snapshot mismatch, failing
docs.spec.tsand, by extension, the functional test job in CI.
Nothing in the existing test setup guards against this — snapshot tests only catch API surface drift if the snapshot is kept in sync, and there's no independent check verifying route registration against the snapshot content. The fix is straightforward: regenerate the snapshot (e.g. cd apps/api && npx vitest run test/functional/docs.spec.ts -u, or the project's test:functional -- -u equivalent) and commit the updated .snap file alongside this PR's other route-removal changes, so CI reflects the new, smaller API surface.
This is a concrete, reproducible functional-test failure that will block CI on this PR as-is, not a stylistic or documentation nit, so it should be fixed before merge.
| async startFreeTrial() { | ||
| await this.page.getByRole("button", { name: /start free trial/i }).click(); | ||
| } |
There was a problem hiding this comment.
🔴 OnboardingPage.startFreeTrial() (still calls getByRole('button', {name: /start free trial/i})) targets a button that only ever existed in FreeTrialLandingStep.tsx, which this PR deletes along with the entire /signup wizard. The new /onboarding picker has no such button, so registerWithEmailPassword in tests/ui/actions/auth.ts:113 (the password-signup E2E flow), which calls startFreeTrial() right after signup, will hang until Playwright's action timeout and fail. Please remove startFreeTrial() from OnboardingPage.ts and drop the now-unnecessary call in auth.ts.
Extended reasoning...
The bug: apps/deploy-web/tests/ui/pages/OnboardingPage.ts still exposes startFreeTrial():
async startFreeTrial() {
await this.page.getByRole("button", { name: /start free trial/i }).click();
}That locator only ever matched one element in the app: the <Button ... onClick={onStartTrial}>Start Free Trial</Button> rendered by FreeTrialLandingStep.tsx. This PR deletes FreeTrialLandingStep.tsx outright, along with the rest of the /signup onboarding wizard (OnboardingContainer, OnboardingView, OnboardingStepper, the pages/signup/index.tsx route, etc.) — trial provisioning is now handled server-side off registration/verification, and the client-side "click start trial" step no longer exists anywhere in the product.
Where it breaks: apps/deploy-web/tests/ui/actions/auth.ts (untouched by this PR) still drives the password-signup E2E flow through registerWithEmailPassword:
await auth.signUp({ email, password: generateTestPassword() });
await signupResponse;
...
await new OnboardingPage(page).startFreeTrial(); // line 113
await deps.emailVerification.verify(...);Why nothing catches this today: the deleted component and the leftover page-object method live in different files, so no compiler or lint pass connects them — a Playwright getByRole locator is just a runtime string match against the DOM, not a typed reference to a component. TypeScript happily compiles OnboardingPage.ts since startFreeTrial() has no dependency on the deleted files. The break only manifests when the E2E suite actually runs this flow against a live app.
Concrete walkthrough of the failure:
registerWithEmailPasswordsubmits the password signup form and awaits the/api/auth/password-signupresponse.- Immediately after, it calls
new OnboardingPage(page).startFreeTrial(). - Post-PR, the user lands on the new
/onboardingpicker (OnboardingPickerPage.tsx), whose only actions are a "Deploy image" link, an add-credits ghost button, andSkipOnboardingButton— none of which match/start free trial/i. - Playwright's
getByRole('button', { name: /start free trial/i }).click()finds zero matching elements and retries until the default action timeout is hit, failing the test with a timeout error rather than a clear "element not found" message. - The rest of
registerWithEmailPassword(email verification, URL assertion) never executes, so the whole password-signup registration E2E path is broken.
Supporting evidence the PR did intend to clean up trial-related page objects: HomePage.ts in this same diff removes its own startTrial() helper and updates isCurrentPage()'s regex from /^\/(onboarding|signup)/ to /^\/onboarding/, showing the author was actively pruning stale trial-flow test helpers — OnboardingPage.startFreeTrial() and its caller were simply missed.
Fix: delete startFreeTrial() from OnboardingPage.ts and remove the now-pointless await new OnboardingPage(page).startFreeTrial(); call in auth.ts's registerWithEmailPassword (trial provisioning happens automatically server-side now, so no client action is needed at that point in the flow).
Why
Closes CON-756
Trial provisioning is fully server-side now — the wallet's created at registration, activation runs off
registration/email-verification, and the UI just polls until it's ready. That leaves the client-side
POST /v1/start-trialcall, and the/signupwizard that used it, as dead weight. This drops both.What
POST /v1/start-trial(router, controller shim, schemas, its unusedcreate-UserWalletability) and its only caller —
WalletProvider/useManagedWalletare report-only now./signuponboarding wizard; onboarding is the/onboardingpicker. KeptRequireOnboardingandVerificationCodeInput(still used by passwordless auth);/user/verify-emailredirects home.
Breaking:
/v1/start-trialis gone (ship API + web together);/signupnow 404s.Summary by CodeRabbit
Breaking Changes
POST /v1/start-trialAPI endpoint.Improvements