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
23 changes: 15 additions & 8 deletions packages/cli/src/test/json-output-validation.mts
Original file line number Diff line number Diff line change
Expand Up @@ -37,49 +37,56 @@ export function validateSocketJson<T = unknown>(
): SocketJsonResponse<T> {
let parsed: any

// Truncate to keep error messages readable; full payload goes in the message.
const preview = jsonString.length > 200
? `${jsonString.slice(0, 200)}...`
: jsonString

// Check if it's valid JSON.
try {
parsed = JSON.parse(jsonString)
} catch (_e) {
throw new Error(`Invalid JSON output: ${jsonString}`)
} catch (e) {
throw new Error(
`command output is not valid JSON (JSON.parse threw: ${e instanceof Error ? e.message : String(e)}); got: ${preview} — check for unclosed braces, trailing commas, or non-JSON text mixed into stdout`,
)
}

// Check for required ok field.
if (typeof parsed.ok !== 'boolean') {
throw new Error(
`JSON output missing required 'ok' boolean field: ${jsonString}`,
`Socket JSON contract violation: missing boolean "ok" field (contract: {ok: boolean, data?: any, message?: string}); got: ${preview} — add ok:true for success, ok:false for failure in the output handler`,
)
}

// Validate based on exit code expectation.
if (expectedExitCode === 0) {
if (parsed.ok !== true) {
throw new Error(
`JSON output 'ok' should be true when exit code is 0: ${jsonString}`,
`Socket JSON contract violation: exit code is 0 but "ok" is ${JSON.stringify(parsed.ok)} (expected true); got: ${preview} — set ok:true when the command succeeds, or return a non-zero exit code`,
)
}
// Success response must have data field.
if (parsed.data === undefined || parsed.data === null) {
throw new Error(
`JSON output missing required 'data' field when ok is true: ${jsonString}`,
`Socket JSON contract violation: ok:true must include a non-null "data" field (got: ${JSON.stringify(parsed.data)}); full output: ${preview} — return an empty object or array instead of undefined/null`,
)
}
} else {
if (parsed.ok !== false) {
throw new Error(
`JSON output 'ok' should be false when exit code is non-zero: ${jsonString}`,
`Socket JSON contract violation: exit code is ${expectedExitCode} but "ok" is ${JSON.stringify(parsed.ok)} (expected false); got: ${preview} — set ok:false on failure, or exit 0 on success`,
)
}
// Error response must have message field.
if (typeof parsed.message !== 'string' || !parsed.message.length) {
throw new Error(
`JSON output missing required 'message' field when ok is false: ${jsonString}`,
`Socket JSON contract violation: ok:false must include a non-empty "message" string (got: ${JSON.stringify(parsed.message)}); full output: ${preview} — provide a user-facing error description`,
)
}
// If code exists, it must be a number.
if (parsed.code !== undefined && typeof parsed.code !== 'number') {
throw new Error(
`JSON output 'code' field must be a number: ${jsonString}`,
`Socket JSON contract violation: "code" field must be a number when present (got: ${typeof parsed.code} ${JSON.stringify(parsed.code)}); full output: ${preview} — drop the field or set it to an integer`,
)
}
}
Expand Down
8 changes: 6 additions & 2 deletions packages/cli/src/test/mocks/socket-auth.mts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ export function mockInteractiveLogin(options?: { shouldSucceed?: boolean }) {
},
}
}
throw new Error('Authentication failed')
throw new Error(
`mock interactive-login rejected (configured with shouldSucceed:false); this is a test fixture — flip shouldSucceed to true when testing the happy path`,
)
})
}

Expand Down Expand Up @@ -118,7 +120,9 @@ export function mockOAuthPoller(options?: {
token: MOCK_API_TOKEN,
}
}
throw new Error('OAuth timeout')
throw new Error(
`mock OAuth poller rejected after ${pollCount} polls (configured with shouldSucceed:false); this is a test fixture — set shouldSucceed:true or adjust pollCount to test the happy path`,
)
})
}

Expand Down
14 changes: 11 additions & 3 deletions packages/cli/test/json-output-validation.mts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,18 @@ export function validateSocketJson(output: string, exitCode: number) {
}
return {
ok: false,
message: parsed.message || parsed.error || 'Unknown error',
message:
parsed.message ||
parsed.error ||
`command exited with code ${exitCode} but returned JSON had no .message or .error field`,
}
} catch (_e) {
} catch (e) {
// If not valid JSON, return error.
return { ok: false, message: 'Invalid JSON output' }
const preview =
output.length > 200 ? `${output.slice(0, 200)}...` : output
return {
ok: false,
message: `command output is not valid JSON (JSON.parse: ${e instanceof Error ? e.message : String(e)}); got: ${preview}`,
}
}
}
12 changes: 6 additions & 6 deletions packages/cli/test/smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ validate_json() {

# First check if it's valid JSON
if ! echo "$json_output" | jq . > /dev/null 2>&1; then
echo -e "${RED}✗ Invalid JSON output${NC}"
echo -e "${RED}✗ command output is not valid JSON (jq rejected it); stdout may contain progress text mixed with the payload${NC}"
echo -e "Received:"
echo -e "$json_output"
return 1
Expand All @@ -115,37 +115,37 @@ validate_json() {

# Check if ok field matches expected exit code
if [ "$expected_exit" -eq 0 ] && [ "$ok_field" != "true" ]; then
echo -e "${RED}✗ JSON output 'ok' should be true when exit code is 0${NC}"
echo -e "${RED}✗ Socket JSON contract violation: exit code 0 but \"ok\" is \"$ok_field\" (expected true); set ok:true on success${NC}"
echo -e "Received:"
echo -e "$json_output"
return 1
fi
if [ "$expected_exit" -ne 0 ] && [ "$ok_field" != "false" ]; then
echo -e "${RED}✗ JSON output 'ok' should be false when exit code is non-zero${NC}"
echo -e "${RED}✗ Socket JSON contract violation: exit code $expected_exit but \"ok\" is \"$ok_field\" (expected false); set ok:false on failure${NC}"
echo -e "Received:"
echo -e "$json_output"
return 1
fi

# Check if data field exists (required when ok is true, optional when false)
if [ "$ok_field" = "true" ] && [ "$data_field" = "null" ]; then
echo -e "${RED}✗ JSON output missing required 'data' field when ok is true${NC}"
echo -e "${RED}✗ Socket JSON contract violation: ok:true must include a non-null \"data\" field; return an empty object/array if there's no payload${NC}"
echo -e "Received:"
echo -e "$json_output"
return 1
fi

# If ok is false, message is required
if [ "$ok_field" = "false" ] && [ -z "$message_field" ]; then
echo -e "${RED}✗ JSON output missing required 'message' field when ok is false${NC}"
echo -e "${RED}✗ Socket JSON contract violation: ok:false must include a non-empty \"message\" string; provide a user-facing error description${NC}"
echo -e "Received:"
echo -e "$json_output"
return 1
fi

# If code exists, it must be a number
if [ -n "$code_field" ] && ! [[ "$code_field" =~ ^[0-9]+$ ]]; then
echo -e "${RED}✗ JSON output 'code' field must be a number${NC}"
echo -e "${RED}✗ Socket JSON contract violation: \"code\" field must be a number when present (got: \"$code_field\"); drop the field or set it to an integer${NC}"
echo -e "Received:"
echo -e "$json_output"
return 1
Expand Down