Skip to content

Commit

Permalink
Updated config, README_EARLY.md and generated unit tests for code-gen…
Browse files Browse the repository at this point in the history
…erator
  • Loading branch information
sharonyb committed Aug 16, 2024
1 parent 146534e commit 2329625
Show file tree
Hide file tree
Showing 12 changed files with 1,133 additions and 1 deletion.
55 changes: 55 additions & 0 deletions README_EARLY.md
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).
7 changes: 7 additions & 0 deletions early.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"generatedTestStructure": "categories",
"testStructureVariant": "siblingFolder",
"showAllTreeSrcFiles": true,
"testFramework": "jest",
"testSuffix": "test"
}
11 changes: 10 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,16 @@ module.exports = {
collectCoverage: true,

// An array of glob patterns indicating a set of files for which coverage information should be collected
collectCoverageFrom: ["**/code-generator/*.{ts,tsx}", "!**/test/**"],
collectCoverageFrom: [
"**/code-generator/**/*.{ts,tsx}",
"!**/test/**",
"!**/code-templates/**",
"!**/index.ts",
"!**/entry-points/cli-entry-point.ts",
"!**/entry-points/interactive-cli.tsx",
],



// The directory where Jest should output its coverage files
coverageDirectory: "test-reports/coverage",
Expand Down
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
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
Loading

0 comments on commit 2329625

Please sign in to comment.