Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions packages/core/src/code_assist/setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,60 @@ describe('setupUser', () => {
});
});

it('should onboard using FREE when allowedTiers contains FREE without isDefault', async () => {
vi.stubEnv('GOOGLE_CLOUD_PROJECT', '');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To prevent test leakage and ensure reliable test execution in environments where GOOGLE_CLOUD_PROJECT_ID might be set, stub both GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_PROJECT_ID to empty strings. Since setupUser falls back to GOOGLE_CLOUD_PROJECT_ID if GOOGLE_CLOUD_PROJECT is empty, failing to stub both can lead to unexpected test failures.

      vi.stubEnv('GOOGLE_CLOUD_PROJECT', '');
      vi.stubEnv('GOOGLE_CLOUD_PROJECT_ID', '');
References
  1. When testing code that depends on environment variables, use vi.stubEnv('NAME', 'value') and avoid modifying process.env directly to prevent test leakage. To unset a variable, use an empty string vi.stubEnv('NAME', ''). (link)

vi.stubEnv('GOOGLE_CLOUD_PROJECT_ID', '');
mockLoad.mockResolvedValue({
allowedTiers: [{ ...mockFreeTier, isDefault: false }],
});
const userData = await setupUser({} as OAuth2Client, mockConfig);
expect(mockOnboardUser).toHaveBeenCalledWith(
expect.objectContaining({
tierId: UserTierId.FREE,
cloudaicompanionProject: undefined,
}),
);
expect(userData).toEqual({
projectId: 'server-project',
userTier: UserTierId.FREE,
userTierName: 'free',
hasOnboardedPreviously: false,
});
});

it('should use the FIRST allowed tier when multiple tiers exist and none has isDefault', async () => {
vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'test-project');
const customTier = {
id: 'custom-tier',
name: 'custom',
isDefault: false,
};
mockLoad.mockResolvedValue({
allowedTiers: [customTier, { ...mockPaidTier, isDefault: false }],
});
const userData = await setupUser({} as OAuth2Client, mockConfig);
expect(mockOnboardUser).toHaveBeenCalledWith(
expect.objectContaining({
tierId: 'custom-tier',
}),
);
expect(userData.userTier).toBe('custom-tier');
});

it('should preserve LEGACY fallback when allowedTiers is empty or undefined', async () => {
vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'test-project');
mockLoad.mockResolvedValue({
allowedTiers: [],
});
const userData = await setupUser({} as OAuth2Client, mockConfig);
expect(mockOnboardUser).toHaveBeenCalledWith(
expect.objectContaining({
tierId: UserTierId.LEGACY,
}),
);
expect(userData.userTier).toBe(UserTierId.LEGACY);
});

it('should use GOOGLE_CLOUD_PROJECT when onboard response has no project ID', async () => {
vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'test-project');
mockLoad.mockResolvedValue({
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/code_assist/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ function getOnboardTier(res: LoadCodeAssistResponse): GeminiUserTier {
return tier;
}
}

const firstTier = res.allowedTiers?.[0];
if (firstTier) {
return firstTier;
}

return {
name: '',
description: '',
Expand Down
Loading