Skip to content

Commit 1b3cce6

Browse files
committed
refactor: eliminate duplicate code in tests and clean up codebase
Major test refactoring to follow DRY principles: - Created comprehensive test helpers in tests/test-helpers.ts - Socket.IO mock helpers (createMockIO, createMockSocket, etc.) - Config file management helpers (createConfigFileManager) - Combined environment/config setup helper (setupTestEnvironment) - Refactored socket.test.ts to use shared helpers (~70 lines removed) - Updated socket-contracts.test.ts to use shared mocks (~30 lines removed) - Simplified config.test.ts with config file manager (~25 lines removed) - Fixed duplicate type re-export in app/envConfig.ts - Removed duplicate rules in eslint.config.mjs - Overall ~60% reduction in test setup duplication - All 82 tests passing with zero failures Additional cleanup: - Simplified app/routes.ts POST auth handler - Enhanced examples/sso-bigip-apm.html with proper iframe handling - Updated sonar-project.properties with exclusions - Added temporary config.json for testing The refactoring maintains 100% test compatibility while significantly improving code maintainability and consistency across the test suite.
1 parent 4d536cc commit 1b3cce6

File tree

11 files changed

+447
-501
lines changed

11 files changed

+447
-501
lines changed

app/envConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
const debug = createNamespacedDebug('envConfig')
1515

1616
// Re-export types for backward compatibility
17-
export type { EnvValueType, EnvVarMap } from './config/index.js'
17+
export type { EnvValueType, EnvVarMap }
1818

1919

2020
export function loadEnvironmentConfig(): Record<string, unknown> {

app/routes.ts

Lines changed: 112 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,116 @@ type ReqWithSession = Request & {
4343
headers: Record<string, unknown>
4444
}
4545

46+
/**
47+
* Handles SSH validation failure responses based on error type
48+
* Pure function that returns response data without side effects
49+
*/
50+
function createSshValidationErrorResponse(validationResult: {
51+
errorType?: string
52+
errorMessage?: string
53+
}, host: string, port: number): {
54+
status: number
55+
headers?: Record<string, string>
56+
message: string
57+
} {
58+
switch (validationResult.errorType) {
59+
case 'auth':
60+
// Authentication failed - allow re-authentication
61+
return {
62+
status: HTTP.UNAUTHORIZED,
63+
headers: { [HTTP.AUTHENTICATE]: HTTP.REALM },
64+
message: 'SSH authentication failed'
65+
}
66+
case 'network':
67+
// Network/connectivity issue - no point in re-authenticating
68+
return {
69+
status: 502,
70+
message: `Bad Gateway: Unable to connect to SSH server at ${host}:${port} - ${validationResult.errorMessage}`
71+
}
72+
case 'timeout':
73+
// Connection timeout
74+
return {
75+
status: 504,
76+
message: `Gateway Timeout: SSH connection to ${host}:${port} timed out`
77+
}
78+
case undefined:
79+
case 'unknown':
80+
default:
81+
// Unknown error - return 502 as it's likely a connectivity issue
82+
return {
83+
status: 502,
84+
message: `Bad Gateway: SSH connection failed - ${validationResult.errorMessage}`
85+
}
86+
}
87+
}
88+
89+
/**
90+
* Common SSH route handler logic
91+
* Handles validation, authentication, and connection setup
92+
*/
93+
async function handleSshRoute(
94+
req: ReqWithSession,
95+
res: Response,
96+
connectionParams: {
97+
host: string
98+
port: number
99+
term: string | null
100+
},
101+
config: Config
102+
): Promise<void> {
103+
// Get credentials from session (set by auth middleware)
104+
const sshCredentials = req.session.sshCredentials
105+
if (sshCredentials?.username == null || sshCredentials.username === '' ||
106+
sshCredentials.password == null || sshCredentials.password === '') {
107+
debug('Missing SSH credentials in session')
108+
res.setHeader(HTTP.AUTHENTICATE, HTTP.REALM)
109+
res.status(HTTP.UNAUTHORIZED).send('Missing SSH credentials')
110+
return
111+
}
112+
113+
// Validate SSH credentials immediately
114+
const validationResult = await validateSshCredentials(
115+
connectionParams.host,
116+
connectionParams.port,
117+
sshCredentials.username,
118+
sshCredentials.password,
119+
config
120+
)
121+
122+
if (validationResult.success === false) {
123+
debug(
124+
`SSH validation failed for ${sshCredentials.username}@${connectionParams.host}:${connectionParams.port}: ${validationResult.errorType} - ${validationResult.errorMessage}`
125+
)
126+
127+
// Get error response data
128+
const errorResponse = createSshValidationErrorResponse(
129+
validationResult,
130+
connectionParams.host,
131+
connectionParams.port
132+
)
133+
134+
// Set headers if provided
135+
if (errorResponse.headers != null) {
136+
Object.entries(errorResponse.headers).forEach(([key, value]) => {
137+
res.setHeader(key, value)
138+
})
139+
}
140+
141+
res.status(errorResponse.status).send(errorResponse.message)
142+
return
143+
}
144+
145+
// SSH validation succeeded - proceed with normal flow
146+
processAuthParameters(req.query, req.session)
147+
const sanitizedCredentials = setupSshCredentials(req.session, connectionParams)
148+
debug('SSH validation passed - serving client: ', sanitizedCredentials)
149+
void handleConnection(
150+
req as unknown as Request & { session?: Record<string, unknown>; sessionID?: string },
151+
res,
152+
{ host: connectionParams.host }
153+
)
154+
}
155+
46156

47157
/**
48158
* Validate SSH credentials by attempting a connection
@@ -89,72 +199,7 @@ export function createRoutes(config: Config): Router {
89199
config,
90200
})
91201

92-
// Get credentials from session (set by auth middleware)
93-
const sshCredentials = r.session.sshCredentials
94-
if (sshCredentials?.username == null || sshCredentials.username === '' || sshCredentials.password == null || sshCredentials.password === '') {
95-
debug('Missing SSH credentials in session')
96-
res.setHeader(HTTP.AUTHENTICATE, HTTP.REALM)
97-
res.status(HTTP.UNAUTHORIZED).send('Missing SSH credentials')
98-
return
99-
}
100-
101-
// Validate SSH credentials immediately
102-
const validationResult = await validateSshCredentials(
103-
host,
104-
port,
105-
sshCredentials.username,
106-
sshCredentials.password,
107-
config
108-
)
109-
110-
if (validationResult.success === false) {
111-
debug(
112-
`SSH validation failed for ${sshCredentials.username}@${host}:${port}: ${validationResult.errorType} - ${validationResult.errorMessage}`
113-
)
114-
115-
// Return appropriate status code based on error type
116-
switch (validationResult.errorType) {
117-
case 'auth':
118-
// Authentication failed - allow re-authentication
119-
res.setHeader(HTTP.AUTHENTICATE, HTTP.REALM)
120-
res.status(HTTP.UNAUTHORIZED).send('SSH authentication failed')
121-
break
122-
case 'network':
123-
// Network/connectivity issue - no point in re-authenticating
124-
res
125-
.status(502)
126-
.send(
127-
`Bad Gateway: Unable to connect to SSH server at ${host}:${port} - ${validationResult.errorMessage}`
128-
)
129-
break
130-
case 'timeout':
131-
// Connection timeout
132-
res.status(504).send(`Gateway Timeout: SSH connection to ${host}:${port} timed out`)
133-
break
134-
case undefined:
135-
case 'unknown':
136-
default:
137-
// Unknown error - return 502 as it's likely a connectivity issue
138-
res
139-
.status(502)
140-
.send(`Bad Gateway: SSH connection failed - ${validationResult.errorMessage}`)
141-
}
142-
return
143-
}
144-
145-
// SSH validation succeeded - proceed with normal flow
146-
processAuthParameters(r.query, r.session)
147-
const sanitizedCredentials = setupSshCredentials(r.session, {
148-
host,
149-
port,
150-
term,
151-
})
152-
debug('/ssh/host/ SSH validation passed - serving client: ', sanitizedCredentials)
153-
void handleConnection(
154-
req as unknown as Request & { session?: Record<string, unknown>; sessionID?: string },
155-
res,
156-
{ host }
157-
)
202+
await handleSshRoute(r, res, { host, port, term }, config)
158203
} catch (err) {
159204
handleRouteError(err as Error, res)
160205
}
@@ -175,72 +220,7 @@ export function createRoutes(config: Config): Router {
175220
config,
176221
})
177222

178-
// Get credentials from session (set by auth middleware)
179-
const sshCredentials = r.session.sshCredentials
180-
if (sshCredentials?.username == null || sshCredentials.username === '' || sshCredentials.password == null || sshCredentials.password === '') {
181-
debug('Missing SSH credentials in session')
182-
res.setHeader(HTTP.AUTHENTICATE, HTTP.REALM)
183-
res.status(HTTP.UNAUTHORIZED).send('Missing SSH credentials')
184-
return
185-
}
186-
187-
// Validate SSH credentials immediately
188-
const validationResult = await validateSshCredentials(
189-
host,
190-
port,
191-
sshCredentials.username,
192-
sshCredentials.password,
193-
config
194-
)
195-
196-
if (validationResult.success === false) {
197-
debug(
198-
`SSH validation failed for ${sshCredentials.username}@${host}:${port}: ${validationResult.errorType} - ${validationResult.errorMessage}`
199-
)
200-
201-
// Return appropriate status code based on error type
202-
switch (validationResult.errorType) {
203-
case 'auth':
204-
// Authentication failed - allow re-authentication
205-
res.setHeader(HTTP.AUTHENTICATE, HTTP.REALM)
206-
res.status(HTTP.UNAUTHORIZED).send('SSH authentication failed')
207-
break
208-
case 'network':
209-
// Network/connectivity issue - no point in re-authenticating
210-
res
211-
.status(502)
212-
.send(
213-
`Bad Gateway: Unable to connect to SSH server at ${host}:${port} - ${validationResult.errorMessage}`
214-
)
215-
break
216-
case 'timeout':
217-
// Connection timeout
218-
res.status(504).send(`Gateway Timeout: SSH connection to ${host}:${port} timed out`)
219-
break
220-
case undefined:
221-
case 'unknown':
222-
default:
223-
// Unknown error - return 502 as it's likely a connectivity issue
224-
res
225-
.status(502)
226-
.send(`Bad Gateway: SSH connection failed - ${validationResult.errorMessage}`)
227-
}
228-
return
229-
}
230-
231-
// SSH validation succeeded - proceed with normal flow
232-
processAuthParameters(r.query, r.session)
233-
const sanitizedCredentials = setupSshCredentials(r.session, {
234-
host,
235-
port,
236-
term,
237-
})
238-
debug('/ssh/host/:host SSH validation passed - serving client: ', sanitizedCredentials)
239-
void handleConnection(
240-
req as unknown as Request & { session?: Record<string, unknown>; sessionID?: string },
241-
res,
242-
{ host }
243-
)
223+
await handleSshRoute(r, res, { host, port, term }, config)
244224
} catch (err) {
245225
handleRouteError(err as Error, res)
246226
}

config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"listen": {
3+
"ip": "127.0.0.1",
4+
"port": 3000
5+
},
6+
"ssh": {
7+
"port": 22,
8+
"term": "xterm-256color"
9+
}
10+
}

0 commit comments

Comments
 (0)