1- import * as fs from "fs" ;
2- import * as os from "os" ;
3- import * as path from "path" ;
4-
51import { afterEach , beforeEach , describe , expect , test , vi } from "vitest" ;
62
73import type { AuthConfig } from "./auth/workos.js" ;
8- import { runNormalFlow } from "./onboarding.js" ;
9-
10- describe ( "onboarding config flag handling" , ( ) => {
11- let tempDir : string ;
12- let mockAuthConfig : AuthConfig ;
13-
14- beforeEach ( ( ) => {
15- // Create a temporary directory for test config files
16- tempDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "continue-test-" ) ) ;
17-
18- // Create a minimal auth config for testing
19- mockAuthConfig = {
20- userId : "test-user" ,
21- 22- accessToken : "test-token" ,
23- refreshToken : "test-refresh" ,
24- expiresAt : Date . now ( ) + 3600000 ,
25- organizationId : "test-org" ,
26- } ;
27- } ) ;
28-
29- afterEach ( ( ) => {
30- // Clean up temporary directory
31- if ( fs . existsSync ( tempDir ) ) {
32- fs . rmSync ( tempDir , { recursive : true , force : true } ) ;
33- }
34- } ) ;
35-
36- test ( "should fail loudly when --config points to non-existent file" , async ( ) => {
37- const configPath = path . join ( tempDir , "non-existent.yaml" ) ;
38-
39- // Verify the file doesn't exist
40- expect ( fs . existsSync ( configPath ) ) . toBe ( false ) ;
41-
42- // Should throw an error that mentions both the path and the failure
43- await expect ( runNormalFlow ( mockAuthConfig , configPath ) ) . rejects . toThrow (
44- / F a i l e d t o l o a d c o n f i g f r o m " .* n o n - e x i s t e n t \. y a m l " : .* E N O E N T / ,
45- ) ;
46- } ) ;
47-
48- test ( "should fail loudly when --config points to malformed YAML file" , async ( ) => {
49- const configPath = path . join ( tempDir , "malformed.yaml" ) ;
50-
51- // Create a malformed YAML file
52- fs . writeFileSync (
53- configPath ,
54- `
55- name: "Test Config"
56- models:
57- - name: "GPT-4"
58- provider: "openai"
59- invalid_yaml_syntax: [unclosed array
60- ` ,
61- ) ;
62-
63- // Verify the file exists
64- expect ( fs . existsSync ( configPath ) ) . toBe ( true ) ;
65-
66- // Should throw an error mentioning the path and failure to load
67- await expect ( runNormalFlow ( mockAuthConfig , configPath ) ) . rejects . toThrow (
68- / F a i l e d t o l o a d c o n f i g f r o m " .* m a l f o r m e d \. y a m l " : .+ / ,
69- ) ;
70- } ) ;
71-
72- test ( "should fail loudly when --config points to file with missing required fields" , async ( ) => {
73- const configPath = path . join ( tempDir , "incomplete.yaml" ) ;
74-
75- // Create a config file missing required fields
76- fs . writeFileSync (
77- configPath ,
78- `
79- name: "Incomplete Config"
80- # Missing models array and other required fields
81- ` ,
82- ) ;
83-
84- // Verify the file exists
85- expect ( fs . existsSync ( configPath ) ) . toBe ( true ) ;
86-
87- // Should throw with our specific error format and include path
88- await expect ( runNormalFlow ( mockAuthConfig , configPath ) ) . rejects . toThrow (
89- / ^ F a i l e d t o l o a d c o n f i g f r o m " .* " : .+ / ,
90- ) ;
91- } ) ;
92-
93- test ( "should handle different config path formats with proper error messages" , async ( ) => {
94- const testPaths = [
95- "./non-existent.yaml" ,
96- "/absolute/path/config.yaml" ,
97- "../relative/config.yaml" ,
98- "simple-name.yaml" ,
99- ] ;
100-
101- for ( const configPath of testPaths ) {
102- await expect ( runNormalFlow ( mockAuthConfig , configPath ) ) . rejects . toThrow (
103- / F a i l e d t o l o a d c o n f i g f r o m " .* " : .+ / ,
104- ) ;
105- }
106- } ) ;
107-
108- test ( "should handle empty string config path" , async ( ) => {
109- // Empty string should be treated differently from undefined
110- // Note: empty string triggers onboarding flow, but should still fail in our error format
111- await expect ( runNormalFlow ( mockAuthConfig , "" ) ) . rejects . toThrow ( ) ;
112- } ) ;
113-
114- test ( "should not fall back to default config when explicit config fails" , async ( ) => {
115- const configPath = path . join ( tempDir , "bad-config.yaml" ) ;
116-
117- // Create a bad config file
118- fs . writeFileSync ( configPath , "invalid: yaml: content: [" ) ;
119-
120- const promise = runNormalFlow ( mockAuthConfig , configPath ) ;
121-
122- await expect ( promise ) . rejects . toThrow ( ) ;
123-
124- try {
125- await promise ;
126- } catch ( error ) {
127- const message = error instanceof Error ? error . message : String ( error ) ;
128-
129- // CRITICAL: Must have our specific error format from the fix
130- expect ( message ) . toMatch ( / ^ F a i l e d t o l o a d c o n f i g f r o m " .* " : .+ / ) ;
131-
132- // Error should be about the specific config file we provided
133- expect ( message ) . toContain ( configPath ) ;
134-
135- // Should NOT mention falling back to default config (this was the bug!)
136- expect ( message ) . not . toContain ( "~/.continue/config.yaml" ) ;
137- expect ( message ) . not . toContain ( "default config" ) ;
138- expect ( message ) . not . toContain ( "fallback" ) ;
139- }
140- } ) ;
141-
142- test ( "demonstrates the fix: explicit config failure vs no config provided" , async ( ) => {
143- const badConfigPath = path . join ( tempDir , "bad.yaml" ) ;
144- fs . writeFileSync ( badConfigPath , "invalid yaml [" ) ;
145-
146- // Case 1: Explicit --config that fails should throw our specific error
147- await expect ( runNormalFlow ( mockAuthConfig , badConfigPath ) ) . rejects . toThrow (
148- / ^ F a i l e d t o l o a d c o n f i g f r o m " / ,
149- ) ;
150-
151- // Case 2: No explicit config should follow different logic
152- try {
153- await runNormalFlow ( mockAuthConfig , undefined ) ;
154- // If it succeeds, that's fine - the point is it's different behavior
155- } catch ( error ) {
156- const errorMessage =
157- error instanceof Error ? error . message : String ( error ) ;
158- // This should NOT have our "Failed to load config from" prefix
159- expect ( errorMessage ) . not . toMatch ( / ^ F a i l e d t o l o a d c o n f i g f r o m " / ) ;
160- }
161- } ) ;
162- } ) ;
1634
1645// Separate describe block with its own mocking for BEDROCK tests
1656describe ( "CONTINUE_USE_BEDROCK environment variable" , ( ) => {
@@ -213,9 +54,9 @@ describe("CONTINUE_USE_BEDROCK environment variable", () => {
21354 vi . resetModules ( ) ;
21455 const { runOnboardingFlow } = await import ( "./onboarding.js" ) ;
21556
216- const result = await runOnboardingFlow ( undefined , mockAuthConfig ) ;
57+ const result = await runOnboardingFlow ( undefined ) ;
21758
218- expect ( result . wasOnboarded ) . toBe ( true ) ;
59+ expect ( result ) . toBe ( true ) ;
21960 expect ( mockConsoleLog ) . toHaveBeenCalledWith (
22061 expect . stringContaining (
22162 "✓ Using AWS Bedrock (CONTINUE_USE_BEDROCK detected)" ,
@@ -235,7 +76,7 @@ describe("CONTINUE_USE_BEDROCK environment variable", () => {
23576 process . stdin . isTTY = false ;
23677
23778 try {
238- await runOnboardingFlow ( undefined , mockAuthConfig ) ;
79+ await runOnboardingFlow ( undefined ) ;
23980
24081 // Verify the Bedrock message was NOT called by checking all calls
24182 const allCalls = mockConsoleLog . mock . calls . flat ( ) ;
0 commit comments