-
Notifications
You must be signed in to change notification settings - Fork 135
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Updated config, README_EARLY.md and generated unit tests for code-gen…
…erator
- Loading branch information
Showing
12 changed files
with
1,133 additions
and
1 deletion.
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 |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# EarlyAI unit test summary | ||
|
||
## Configuration | ||
The repository was already configured to work with JEST. | ||
The tests were created with JEST testing framework. | ||
No special configuration is needed to setup the project except updating jest.config.js collectCoverageFrom: | ||
|
||
Original configuration: | ||
collectCoverageFrom: [ | ||
"**/code-generator/**/*.{ts,tsx}", | ||
"!**/test/**" | ||
], | ||
|
||
Updated to this one to collect coverage correctly where applicable. | ||
collectCoverageFrom: [ | ||
"**/code-generator/**/*.{ts,tsx}", | ||
"!**/test/**", | ||
"!**/code-templates/**", | ||
"!**/index.ts", | ||
"!**/entry-points/cli-entry-point.ts", | ||
"!**/entry-points/interactive-cli.tsx", | ||
], | ||
|
||
|
||
## Target code | ||
src/code-generator | ||
src/code-templates was excluded from testing by the author so it was excluded from this tests run. | ||
|
||
## cleanup | ||
For the purposed of this exercise we .skip all existing tests on files so we can see the impact of the newly generated tests by EarlyAI: | ||
|
||
practica/src/code-generator/generation-logic/generate-service.test.ts | ||
practica/src/code-generator/test/generator.non-interactive-cli.test.ts | ||
|
||
and all tests on files under: | ||
practica/test/ | ||
|
||
## Results | ||
### src/code-generator Coverage - 96% | ||
### Passed | ||
Test Suites: 9 (Files or describe blocks) | ||
Tests: 48 | ||
### Failed | ||
Tests: 0 | ||
We did not generate any red tests because this is more of a how-to or example repo rather than a real functional code. | ||
|
||
|
||
|
||
### About Early | ||
Early leverages Generative AI to accelerate development, enhance code quality, and speed up time-to-market. Our AI-driven product generates automated, comprehensive, cost-effective working unit tests, and help catch bugs early, expanding code coverage, and improving overall quality | ||
|
||
|
||
Learn more at www.startearly.ai or search for EarlyAI on VSCode marketplace, install and user EarlyAI extension to generate unit tests in a click. | ||
|
||
Read more on our [blogs](https://www.startearly.ai/early-blog). |
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,7 @@ | ||
{ | ||
"generatedTestStructure": "categories", | ||
"testStructureVariant": "siblingFolder", | ||
"showAllTreeSrcFiles": true, | ||
"testFramework": "jest", | ||
"testSuffix": "test" | ||
} |
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
144 changes: 144 additions & 0 deletions
144
...tor/entry-points/non-interactive-cli.early.test/handleNonInteractiveCommand.early.test.ts
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,144 @@ | ||
// Unit tests for: handleNonInteractiveCommand | ||
|
||
import { AppError } from "../../error-handling"; | ||
import { factorDefaultOptions } from "../../generation-logic/generation-options"; | ||
import { generateApp } from "../../generation-logic/generate-service"; | ||
import { nonInteractiveCliTexts, spinner } from "../ui-elements"; | ||
import { handleNonInteractiveCommand } from "../non-interactive-cli"; | ||
|
||
jest.mock("../../generation-logic/generation-options"); | ||
jest.mock("../../generation-logic/generate-service"); | ||
jest.mock("../ui-elements"); | ||
|
||
// @ts-ignore | ||
jest.spyOn(process, 'exit').mockImplementation(() => { }); | ||
|
||
describe("handleNonInteractiveCommand() handleNonInteractiveCommand method", () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe("Happy Path", () => { | ||
it("should start the spinner, generate the app, and succeed", async () => { | ||
// Arrange | ||
const options = { | ||
installDependencies: true, | ||
overrideIfExists: false, | ||
orm: "typeorm", | ||
webFramework: "express", | ||
targetDirectory: "/my/app", | ||
appName: "myApp", | ||
}; | ||
(factorDefaultOptions as jest.Mock).mockReturnValue(options); | ||
(generateApp as jest.Mock).mockResolvedValue(undefined); | ||
|
||
// Act | ||
await handleNonInteractiveCommand(options); | ||
|
||
// Assert | ||
expect(spinner.start).toHaveBeenCalledWith( | ||
nonInteractiveCliTexts.onStart | ||
); | ||
expect(generateApp).toHaveBeenCalledWith(options); | ||
expect(spinner.succeed).toHaveBeenCalledWith( | ||
nonInteractiveCliTexts.onSucceed | ||
); | ||
}); | ||
}); | ||
|
||
describe("Edge Cases", () => { | ||
it("should handle missing targetDirectory by using process.cwd()", async () => { | ||
// Arrange | ||
const options = { | ||
installDependencies: true, | ||
overrideIfExists: false, | ||
orm: "typeorm", | ||
webFramework: "express", | ||
appName: "myApp", | ||
}; | ||
const cwd = process.cwd(); | ||
(factorDefaultOptions as jest.Mock).mockReturnValue(options); | ||
(generateApp as jest.Mock).mockResolvedValue(undefined); | ||
|
||
// Act | ||
await handleNonInteractiveCommand(options); | ||
|
||
// Assert | ||
expect(factorDefaultOptions).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
targetDirectory: cwd, | ||
}) | ||
); | ||
}); | ||
|
||
it("should handle errors thrown by generateApp", async () => { | ||
// Arrange | ||
const options = { | ||
installDependencies: true, | ||
overrideIfExists: false, | ||
orm: "typeorm", | ||
webFramework: "express", | ||
targetDirectory: "/my/app", | ||
appName: "myApp", | ||
}; | ||
const error = new AppError("generation-failed", "Generation failed"); | ||
(factorDefaultOptions as jest.Mock).mockReturnValue(options); | ||
(generateApp as jest.Mock).mockRejectedValue(error); | ||
|
||
// Act | ||
await handleNonInteractiveCommand(options); | ||
|
||
// Assert | ||
expect(spinner.fail).toHaveBeenCalledWith("Generation failed"); | ||
expect(process.exit).toHaveBeenCalledWith(1); | ||
}); | ||
|
||
it("should handle generic errors gracefully", async () => { | ||
// Arrange | ||
const options = { | ||
installDependencies: true, | ||
overrideIfExists: false, | ||
orm: "typeorm", | ||
webFramework: "express", | ||
targetDirectory: "/my/app", | ||
appName: "myApp", | ||
}; | ||
const error = new Error("Some unexpected error"); | ||
(factorDefaultOptions as jest.Mock).mockReturnValue(options); | ||
(generateApp as jest.Mock).mockRejectedValue(error); | ||
|
||
// Act | ||
await handleNonInteractiveCommand(options); | ||
|
||
// Assert | ||
expect(spinner.fail).toHaveBeenCalledWith("Some unexpected error"); | ||
expect(process.exit).toHaveBeenCalledWith(1); | ||
}); | ||
|
||
it("should use default error message if error has no message", async () => { | ||
// Arrange | ||
const options = { | ||
installDependencies: true, | ||
overrideIfExists: false, | ||
orm: "typeorm", | ||
webFramework: "express", | ||
targetDirectory: "/my/app", | ||
appName: "myApp", | ||
}; | ||
const error = new Error(); | ||
(factorDefaultOptions as jest.Mock).mockReturnValue(options); | ||
(generateApp as jest.Mock).mockRejectedValue(error); | ||
|
||
// Act | ||
await handleNonInteractiveCommand(options); | ||
|
||
// Assert | ||
expect(spinner.fail).toHaveBeenCalledWith( | ||
nonInteractiveCliTexts.onError.default | ||
); | ||
expect(process.exit).toHaveBeenCalledWith(1); | ||
}); | ||
}); | ||
}); | ||
|
||
// End of unit tests for: handleNonInteractiveCommand |
111 changes: 111 additions & 0 deletions
111
src/code-generator/generation-logic/features/choose-orm.early.test/chooseORM.early.test.ts
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,111 @@ | ||
// Unit tests for: chooseORM | ||
|
||
import * as fsExtra from "fs-extra"; | ||
import path from "path"; | ||
|
||
import { generationOptions } from "../../generation-options"; | ||
import { | ||
getMicroservicePath, | ||
replacePhraseInFile, | ||
} from "../../string-manipulation-helpers"; | ||
import { chooseORM } from "../choose-orm"; | ||
|
||
jest.mock('fs-extra'); | ||
|
||
jest.mock("../../string-manipulation-helpers", () => { | ||
const actual = jest.requireActual("../../string-manipulation-helpers"); // This fetches the actual implementations | ||
|
||
return { | ||
...actual, // Uses the actual implementations | ||
getMicroservicePath: jest.fn(), | ||
replacePhraseInFile: jest.fn(), | ||
}; | ||
}); | ||
|
||
describe("chooseORM() chooseORM method", () => { | ||
const generatedAppRoot = "mock/generated/app/root"; | ||
const mockMicroservicePath = "mock/microservice/path"; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe("Happy Path", () => { | ||
it("should adjust the code to Prisma ORM", async () => { | ||
(getMicroservicePath as jest.Mock).mockReturnValue(mockMicroservicePath); | ||
|
||
const options: generationOptions = { | ||
appName: "testApp", | ||
ORM: "prisma", | ||
webFramework: "express", | ||
DBType: "postgres", | ||
mainMicroserviceName: "mainService", | ||
emitBestPracticesHints: true, | ||
targetDirectory: "target/dir", | ||
installDependencies: true, | ||
overrideIfExists: false, | ||
}; | ||
(replacePhraseInFile as jest.Mock).mockResolvedValue(undefined); | ||
|
||
await chooseORM(generatedAppRoot, options); | ||
|
||
expect(getMicroservicePath).toHaveBeenCalledWith(generatedAppRoot); | ||
expect(fsExtra.rm).toHaveBeenCalledWith( | ||
path.join(mockMicroservicePath, "data-access"), | ||
{ recursive: true } | ||
); | ||
expect(fsExtra.rename).toHaveBeenCalledWith( | ||
path.join(mockMicroservicePath, "data-access-prisma"), | ||
path.join(mockMicroservicePath, "data-access") | ||
); | ||
expect(replacePhraseInFile).toHaveBeenCalledTimes(4); | ||
}); | ||
|
||
it("should adjust the code to Sequelize ORM", async () => { | ||
const options: generationOptions = { | ||
appName: "testApp", | ||
ORM: "sequelize", | ||
webFramework: "express", | ||
DBType: "postgres", | ||
mainMicroserviceName: "mainService", | ||
emitBestPracticesHints: true, | ||
targetDirectory: "target/dir", | ||
installDependencies: true, | ||
overrideIfExists: false, | ||
}; | ||
(replacePhraseInFile as jest.Mock).mockResolvedValue(undefined); | ||
|
||
await chooseORM(generatedAppRoot, options); | ||
|
||
expect(getMicroservicePath).toHaveBeenCalledWith(generatedAppRoot); | ||
expect(fsExtra.rm).toHaveBeenCalledWith( | ||
path.join(mockMicroservicePath, "data-access-prisma"), | ||
{ recursive: true } | ||
); | ||
expect(replacePhraseInFile).toHaveBeenCalledTimes(3); // Expecting 3 calls for Sequelize adjustments | ||
}); | ||
}); | ||
|
||
describe("Edge Cases", () => { | ||
it("should handle an unsupported ORM gracefully", async () => { | ||
const options: generationOptions = { | ||
appName: "testApp", | ||
ORM: "unsupported" as any, // Simulating an unsupported ORM | ||
webFramework: "express", | ||
DBType: "postgres", | ||
mainMicroserviceName: "mainService", | ||
emitBestPracticesHints: true, | ||
targetDirectory: "target/dir", | ||
installDependencies: true, | ||
overrideIfExists: false, | ||
}; | ||
|
||
await expect(chooseORM(generatedAppRoot, options)).resolves.not.toThrow(); | ||
expect(getMicroservicePath).toHaveBeenCalledWith(generatedAppRoot); | ||
expect(fsExtra.rm).not.toHaveBeenCalled(); // No folder should be removed | ||
expect(replacePhraseInFile).not.toHaveBeenCalled(); // No phrases should be replaced | ||
}); | ||
}); | ||
}); | ||
|
||
// End of unit tests for: chooseORM |
Oops, something went wrong.