-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: improved error handling and logging
- Loading branch information
ITZSHOAIB
committed
Sep 11, 2024
1 parent
33a53a6
commit 687849c
Showing
7 changed files
with
153 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,97 @@ | ||
import { execSync, spawn } from "child_process"; | ||
import commandExists from "command-exists"; | ||
import { log } from "../internal/utils/logger.js"; | ||
import { CustomError, handleError } from "../internal/utils/errorHandler.js"; | ||
|
||
let anvilProcess: any; | ||
|
||
export const beforeAll = async () => { | ||
log("info", "🚀 Starting test environment..."); | ||
try { | ||
log("info", "🚀 Starting test environment..."); | ||
|
||
const forgeExist = commandExists.sync("forge"); | ||
if (!forgeExist) { | ||
log( | ||
"error", | ||
"❌ Forge is not installed. Please install it first. Please refer to the documentation for more information.\n\nhttps://github.com/ITZSHOAIB/chukti#readme" | ||
); | ||
} | ||
const forgeExist = commandExists.sync("forge"); | ||
if (!forgeExist) { | ||
throw new CustomError( | ||
"Forge is not installed. Please install it first. Refer to the documentation: https://github.com/ITZSHOAIB/chukti#readme" | ||
); | ||
} | ||
|
||
execSync("forge build", { stdio: "inherit" }); | ||
|
||
execSync("forge build", { stdio: "inherit" }); | ||
const isAnvilExist = commandExists.sync("anvil"); | ||
if (!isAnvilExist) { | ||
throw new CustomError( | ||
"Anvil is not installed. Please install it first. Refer to the documentation: https://github.com/ITZSHOAIB/chukti#readme" | ||
); | ||
} | ||
|
||
const isAnvilExist = commandExists.sync("anvil"); | ||
if (!isAnvilExist) { | ||
log( | ||
"error", | ||
"❌ Anvil is not installed. Please install it first. Please refer to the documentation for more information.\n\nhttps://github.com/ITZSHOAIB/chukti#readme" | ||
); | ||
process.exit(1); | ||
await startAnvil(); | ||
} catch (error) { | ||
handleError(error as Error); | ||
} | ||
}; | ||
|
||
const startAnvil = async ( | ||
retries: number = 5, | ||
delay: number = 1000 | ||
): Promise<void> => { | ||
return new Promise<void>((resolve, reject) => { | ||
export const afterAll = () => { | ||
if (anvilProcess) { | ||
anvilProcess.kill(); | ||
anvilProcess.stdout.removeAllListeners(); | ||
anvilProcess.stderr.removeAllListeners(); | ||
anvilProcess.removeAllListeners(); | ||
log("info", "Local blockchain stopped"); | ||
} | ||
}; | ||
|
||
const startAnvil = async ( | ||
retries: number = 5, | ||
delay: number = 1000 | ||
): Promise<void> => { | ||
for (let attempt = 1; attempt <= retries; attempt++) { | ||
try { | ||
anvilProcess = spawn("anvil", { stdio: "pipe" }); | ||
|
||
let stdio = ""; | ||
let stderr = ""; | ||
|
||
const onData = (data: any) => { | ||
anvilProcess.stdout.on("data", (data: Buffer) => { | ||
stdio += data.toString(); | ||
if (stdio.includes("Listening on")) { | ||
cleanup(); | ||
resolve(); | ||
log("success", "✅ Anvil started successfully"); | ||
return; | ||
} | ||
}; | ||
}); | ||
|
||
const onError = (data: any) => { | ||
anvilProcess.stderr.on("data", (data: Buffer) => { | ||
stderr += data.toString(); | ||
log( | ||
"error", | ||
`❌ Error while starting Local blockchain anvil: | ||
stdout: ${stdio} | ||
stderr: ${stderr}` | ||
); | ||
if (retries > 0) { | ||
log( | ||
"info", | ||
`🔄 Retrying to start anvil... (${retries} retries left)` | ||
); | ||
setTimeout( | ||
() => | ||
startAnvil(retries - 1, delay * 2) | ||
.then(resolve) | ||
.catch(reject), | ||
delay | ||
}); | ||
|
||
anvilProcess.on("close", (code: number) => { | ||
if (code !== 0) { | ||
throw new CustomError( | ||
`Anvil process exited with code ${code}: ${stderr}` | ||
); | ||
} else { | ||
cleanup(); | ||
reject(new Error("Failed to start anvil after multiple attempts")); | ||
} | ||
}; | ||
|
||
const onClose = () => { | ||
log("info", "Local blockchain exited"); | ||
}; | ||
|
||
const cleanup = () => { | ||
anvilProcess.stdout.off("data", onData); | ||
anvilProcess.stderr.off("data", onError); | ||
anvilProcess.off("close", onClose); | ||
}; | ||
|
||
anvilProcess.stdout.on("data", onData); | ||
anvilProcess.stderr.on("data", onError); | ||
anvilProcess.on("close", onClose); | ||
}); | ||
}; | ||
}); | ||
|
||
await startAnvil(); | ||
}; | ||
await new Promise((resolve) => setTimeout(resolve, delay)); | ||
|
||
export const afterAll = () => { | ||
anvilProcess.kill(); | ||
log("info", "Local blockchain stopped"); | ||
if (stdio.includes("Listening on")) { | ||
return; | ||
} else { | ||
throw new CustomError("Anvil did not start successfully"); | ||
} | ||
} catch (error) { | ||
log( | ||
"warning", | ||
`⚠️ Attempt ${attempt} to start Anvil failed: ${ | ||
(error as Error).message | ||
}` | ||
); | ||
if (attempt === retries) { | ||
handleError( | ||
new CustomError(`Failed to start Anvil after ${retries} attempts`) | ||
); | ||
} | ||
await new Promise((resolve) => setTimeout(resolve, delay)); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,15 @@ | ||
import { execSync } from "child_process"; | ||
import { log } from "../../utils/logger.js"; | ||
import { CustomError, handleError } from "../../utils/errorHandler.js"; | ||
|
||
export const runTests = async () => { | ||
try { | ||
log("info", "🚀 Running Cucumber tests..."); | ||
execSync("npx cucumber-js", { stdio: "inherit" }); | ||
log("sucess", "✅ Tests completed successfully"); | ||
log("success", "✅ Tests completed successfully"); | ||
} catch (error) { | ||
log("error", `❌ Error during tests: ${(error as Error).message}`); | ||
process.exit(1); | ||
handleError( | ||
new CustomError(`Error before running tests: ${(error as Error).message}`) | ||
); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,46 @@ | ||
import fs from "fs-extra"; | ||
import path from "path"; | ||
import { log } from "../../utils/logger.js"; | ||
import { CustomError, handleError } from "../../utils/errorHandler.js"; | ||
|
||
export const checkChuktiProject = ({ | ||
shouldExist, | ||
}: { | ||
shouldExist: boolean; | ||
}) => { | ||
const chuktiConfigPath = path.join(process.cwd(), "chukti.config.ts"); | ||
|
||
const projectExists = fs.existsSync(chuktiConfigPath); | ||
|
||
if (shouldExist && !projectExists) { | ||
log( | ||
"error", | ||
"❌ Error: A Chukti project does not exist in this directory.\n\nPlease run `chukti init` to initialize a new project." | ||
); | ||
|
||
process.exit(1); | ||
} | ||
|
||
if (!shouldExist && projectExists) { | ||
log( | ||
"error", | ||
"❌ Error: A Chukti project already exists in this directory." | ||
); | ||
try { | ||
const chuktiConfigPath = path.join(process.cwd(), "chukti.config.ts"); | ||
const projectExists = fs.existsSync(chuktiConfigPath); | ||
|
||
if (shouldExist && !projectExists) { | ||
throw new CustomError( | ||
"A Chukti project does not exist in this directory. Please run `chukti init` to initialize a new project." | ||
); | ||
} | ||
|
||
process.exit(1); | ||
if (!shouldExist && projectExists) { | ||
throw new CustomError( | ||
"A Chukti project already exists in this directory." | ||
); | ||
} | ||
} catch (error) { | ||
handleError(error as Error); | ||
} | ||
}; | ||
|
||
export const checkEveryPathExists = (paths: string[]) => { | ||
const errorMessages: string[] = []; | ||
try { | ||
const errorMessages: string[] = []; | ||
|
||
for (const p of paths) { | ||
if (!fs.existsSync(p)) { | ||
errorMessages.push(`❌ Error: ${p} does not exist.`); | ||
for (const p of paths) { | ||
if (!fs.existsSync(p)) { | ||
errorMessages.push(`❌ Error: ${p} does not exist.`); | ||
} | ||
} | ||
} | ||
|
||
if (errorMessages.length > 0) { | ||
log("error", errorMessages.join("\n")); | ||
process.exit(1); | ||
if (errorMessages.length > 0) { | ||
throw new CustomError(errorMessages.join("\n")); | ||
} | ||
} catch (error) { | ||
handleError(error as Error); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { log } from "./logger.js"; | ||
|
||
export class CustomError extends Error { | ||
constructor(message: string) { | ||
super(message); | ||
this.name = this.constructor.name; | ||
} | ||
} | ||
|
||
export const handleError = (error: Error) => { | ||
log("error", `❌ Error: ${error.message}`); | ||
process.exit(1); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,30 @@ | ||
import kleur from "kleur"; | ||
|
||
type LogType = "sucess" | "error" | "warning" | "info"; | ||
type LogType = "success" | "error" | "warning" | "info"; | ||
|
||
export const log = (type: LogType, message: string) => { | ||
const formattedMessage = message.replace(/(https?:\/\/[^\s]+)/g, (url) => | ||
makeUrlClickable(url) | ||
); | ||
|
||
switch (type) { | ||
case "sucess": | ||
console.log(kleur.green(message)); | ||
case "success": | ||
console.log(kleur.green(formattedMessage)); | ||
break; | ||
case "error": | ||
console.error(kleur.red(message)); | ||
console.error(kleur.red(formattedMessage)); | ||
break; | ||
case "warning": | ||
console.warn(kleur.yellow(message)); | ||
console.warn(kleur.yellow(formattedMessage)); | ||
break; | ||
case "info": | ||
console.info(kleur.cyan(message)); | ||
console.info(kleur.cyan(formattedMessage)); | ||
break; | ||
default: | ||
console.log(message); | ||
console.log(formattedMessage); | ||
} | ||
}; | ||
|
||
const makeUrlClickable = (url: string) => { | ||
return `\u001b]8;;${url}\u001b\\${url}\u001b]8;;\u001b\\`; | ||
}; |