Skip to content
This repository was archived by the owner on Mar 23, 2025. It is now read-only.

Commit 0a579b6

Browse files
committed
add error handling to core cli
1 parent 7405dab commit 0a579b6

File tree

4 files changed

+138
-35
lines changed

4 files changed

+138
-35
lines changed

packages/cli/src/core/__tests__/index.test.ts

+85-18
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import DandoriCoreCli from "../index";
1313
import generateDandoriTasks, {
1414
ChatGPTFunctionCallModel,
1515
DandoriTask,
16+
OptionalTaskPropsOption,
1617
} from "@dandori/core";
18+
import { logger } from "@dandori/libs";
1719

1820
const tasks: DandoriTask[] = [
1921
{
@@ -31,6 +33,7 @@ vi.mock("@dandori/core", () => ({
3133

3234
describe("DandoriCoreCli", () => {
3335
const mockConsole = vi.spyOn(console, "log").mockImplementation(() => {});
36+
const mockLogError = vi.spyOn(logger, "error").mockImplementation(() => {});
3437
const inputFileName = "DandoriCoreCli.txt";
3538
const inputFileText = "DandoriCoreCli";
3639
const loadProcessArgv = (options: string[]) => {
@@ -68,18 +71,49 @@ describe("DandoriCoreCli", () => {
6871
});
6972

7073
describe("with -m option", () => {
71-
const model: ChatGPTFunctionCallModel = "gpt-4-0613";
74+
describe("valid argument", () => {
75+
const model: ChatGPTFunctionCallModel = "gpt-4-0613";
7276

73-
beforeEach(async () => {
74-
loadProcessArgv(["-m", model]);
75-
await new DandoriCoreCli().run();
77+
beforeEach(async () => {
78+
loadProcessArgv(["-m", model]);
79+
await new DandoriCoreCli().run();
80+
});
81+
82+
it("call generateDandoriTasks with valid model", () => {
83+
expect(generateDandoriTasks).toHaveBeenCalledWith(inputFileText, {
84+
envFilePath: undefined,
85+
chatGPTModel: model,
86+
optionalTaskProps: undefined,
87+
});
88+
});
7689
});
7790

78-
it("call generateDandoriTasks with envFilePath", () => {
79-
expect(generateDandoriTasks).toHaveBeenCalledWith(inputFileText, {
80-
envFilePath: undefined,
81-
chatGPTModel: model,
82-
optionalTaskProps: undefined,
91+
describe("invalid argument", () => {
92+
const model = "invalid-model";
93+
const supportedChatGPTModels: ChatGPTFunctionCallModel[] = [
94+
"gpt-3.5-turbo-0613",
95+
"gpt-4-0613",
96+
];
97+
const expectedMessage = `Unsupported model: ${model}. Supported models are ${supportedChatGPTModels.join(
98+
", ",
99+
)}`;
100+
101+
beforeEach(() => {
102+
loadProcessArgv(["-m", model]);
103+
});
104+
105+
it("throw Error with valid message", async () => {
106+
await expect(new DandoriCoreCli().run()).rejects.toThrow(
107+
expectedMessage,
108+
);
109+
});
110+
111+
it("call logger.error with valid message", async () => {
112+
try {
113+
await new DandoriCoreCli().run();
114+
} catch {
115+
expect(mockLogError).toHaveBeenCalledWith(expectedMessage);
116+
}
83117
});
84118
});
85119
});
@@ -102,18 +136,51 @@ describe("DandoriCoreCli", () => {
102136
});
103137

104138
describe("with -o option", () => {
105-
const optionalTaskProps = "deadline,description";
139+
describe("valid argument", () => {
140+
const optionalTaskProps = "deadline,description";
106141

107-
beforeEach(async () => {
108-
loadProcessArgv(["-o", optionalTaskProps]);
109-
await new DandoriCoreCli().run();
142+
beforeEach(async () => {
143+
loadProcessArgv(["-o", optionalTaskProps]);
144+
await new DandoriCoreCli().run();
145+
});
146+
147+
it("call generateDandoriTasks with valid optionalTaskProps", () => {
148+
expect(generateDandoriTasks).toHaveBeenCalledWith(inputFileText, {
149+
envFilePath: undefined,
150+
chatGPTModel: undefined,
151+
optionalTaskProps: optionalTaskProps.split(","),
152+
});
153+
});
110154
});
111155

112-
it("call generateDandoriTasks with envFilePath", () => {
113-
expect(generateDandoriTasks).toHaveBeenCalledWith(inputFileText, {
114-
envFilePath: undefined,
115-
chatGPTModel: undefined,
116-
optionalTaskProps: optionalTaskProps.split(","),
156+
describe("invalid argument", () => {
157+
const optionalTaskProps = "invalid";
158+
const supportedOptionalTaskProps: OptionalTaskPropsOption = [
159+
"description",
160+
"deadline",
161+
"assignee",
162+
"all",
163+
];
164+
const expectedMessage = `Unsupported optional task props: ${optionalTaskProps}. Supported optional task props are ${supportedOptionalTaskProps.join(
165+
", ",
166+
)}`;
167+
168+
beforeEach(() => {
169+
loadProcessArgv(["-o", optionalTaskProps]);
170+
});
171+
172+
it("throw Error with valid message", async () => {
173+
await expect(new DandoriCoreCli().run()).rejects.toThrow(
174+
expectedMessage,
175+
);
176+
});
177+
178+
it("call logger.error with valid message", async () => {
179+
try {
180+
await new DandoriCoreCli().run();
181+
} catch {
182+
expect(mockLogError).toHaveBeenCalledWith(expectedMessage);
183+
}
117184
});
118185
});
119186
});

packages/cli/src/core/index.ts

+45-6
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,37 @@ import chalk from "chalk";
44
import generateDandoriTasks, {
55
ChatGPTFunctionCallModel,
66
DandoriTask,
7+
OptionalTaskProps,
78
OptionalTaskPropsOption,
89
} from "@dandori/core";
910
import { readFile } from "fs/promises";
10-
import { loadFile } from "@dandori/libs";
11+
import { loadFile, logger } from "@dandori/libs";
12+
13+
const supportedChatGPTModels: ChatGPTFunctionCallModel[] = [
14+
"gpt-3.5-turbo-0613",
15+
"gpt-4-0613",
16+
];
17+
18+
const isSupportedChatGPTModels = (
19+
model?: string,
20+
): model is ChatGPTFunctionCallModel | undefined =>
21+
model === undefined ||
22+
supportedChatGPTModels.includes(model as ChatGPTFunctionCallModel);
23+
24+
const supportedOptionalTaskProps: OptionalTaskPropsOption = [
25+
"description",
26+
"deadline",
27+
"assignee",
28+
"all",
29+
];
30+
31+
const isSupportedOptionalTaskProps = (
32+
props?: string[],
33+
): props is OptionalTaskPropsOption | undefined =>
34+
props === undefined ||
35+
props.every((prop) =>
36+
supportedOptionalTaskProps.includes(prop as OptionalTaskProps),
37+
);
1138

1239
export default class DandoriCoreCli {
1340
private inputFile: string = "";
@@ -48,15 +75,27 @@ export default class DandoriCoreCli {
4875
const { envFile, optionalTaskProps, model } = this.program.opts<{
4976
envFile?: string;
5077
optionalTaskProps?: string;
51-
model?: ChatGPTFunctionCallModel;
78+
model?: string;
5279
}>();
53-
// TODO: Error Handling of invalid options
80+
if (!isSupportedChatGPTModels(model)) {
81+
const logMessage = `Unsupported model: ${model}. Supported models are ${supportedChatGPTModels.join(
82+
", ",
83+
)}`;
84+
logger.error(logMessage);
85+
throw new Error(logMessage);
86+
}
87+
const inputOptionalTaskProps = optionalTaskProps?.split(",");
88+
if (!isSupportedOptionalTaskProps(inputOptionalTaskProps)) {
89+
const logMessage = `Unsupported optional task props: ${optionalTaskProps}. Supported optional task props are ${supportedOptionalTaskProps.join(
90+
", ",
91+
)}`;
92+
logger.error(logMessage);
93+
throw new Error(logMessage);
94+
}
5495
return generateDandoriTasks(source.toString(), {
5596
envFilePath: envFile,
5697
chatGPTModel: model,
57-
optionalTaskProps: optionalTaskProps?.split(
58-
",",
59-
) as OptionalTaskPropsOption,
98+
optionalTaskProps: inputOptionalTaskProps,
6099
});
61100
}
62101
}

packages/core/src/__tests__/index.test.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import generateDandoriTasks, {
33
DandoriTaskOptionalProperty,
44
DandoriTaskProperty,
55
DandoriTaskRequiredProperty,
6-
OptionalAllDandoriTaskPropertiesName,
6+
DandoriTaskOptionalAllProperty,
77
} from "../index";
88
import { describe, beforeEach, afterEach, it, vi, expect, Mock } from "vitest";
99
import OpenAI from "openai";
@@ -217,9 +217,7 @@ describe("generateDandoriTasks", () => {
217217

218218
describe("with all argument", () => {
219219
const source = "with all argument";
220-
const optionalTaskProps: OptionalAllDandoriTaskPropertiesName[] = [
221-
"all",
222-
];
220+
const optionalTaskProps: DandoriTaskOptionalAllProperty[] = ["all"];
223221

224222
beforeEach(async () => {
225223
result = await generateDandoriTasks(source, {

packages/core/src/index.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,16 @@ export type DandoriTaskOptionalProperty = Exclude<
3131
DandoriTaskRequiredProperty
3232
>;
3333

34-
export type OptionalAllDandoriTaskPropertiesName = "all";
35-
const optionalAllDandoriTaskPropertiesName: OptionalAllDandoriTaskPropertiesName =
36-
"all";
37-
export type OptionalTaskPropsOption = (
34+
export type DandoriTaskOptionalAllProperty = "all";
35+
const dandoriTaskOptionalAllProperty: DandoriTaskOptionalAllProperty = "all";
36+
export type OptionalTaskProps =
3837
| DandoriTaskOptionalProperty
39-
| OptionalAllDandoriTaskPropertiesName
40-
)[];
38+
| DandoriTaskOptionalAllProperty;
39+
export type OptionalTaskPropsOption = OptionalTaskProps[];
4140
const notIncludeAdditionalAllPropsName = (
4241
props: OptionalTaskPropsOption,
4342
): props is DandoriTaskOptionalProperty[] =>
44-
!props.includes(optionalAllDandoriTaskPropertiesName);
43+
!props.includes(dandoriTaskOptionalAllProperty);
4544

4645
export type GenerateDandoriTasksOptions = {
4746
chatGPTModel?: ChatGPTFunctionCallModel;

0 commit comments

Comments
 (0)