@mandujs/cli 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +234 -234
- package/README.md +354 -354
- package/package.json +2 -2
- package/src/commands/contract.ts +173 -173
- package/src/commands/dev.ts +8 -68
- package/src/commands/doctor.ts +27 -27
- package/src/commands/guard-arch.ts +303 -303
- package/src/commands/guard-check.ts +3 -3
- package/src/commands/monitor.ts +300 -300
- package/src/commands/openapi.ts +107 -107
- package/src/commands/registry.ts +367 -357
- package/src/commands/routes.ts +228 -228
- package/src/commands/start.ts +184 -0
- package/src/errors/codes.ts +35 -35
- package/src/errors/index.ts +2 -2
- package/src/errors/messages.ts +143 -143
- package/src/hooks/index.ts +17 -17
- package/src/hooks/preaction.ts +256 -256
- package/src/main.ts +37 -34
- package/src/terminal/banner.ts +166 -166
- package/src/terminal/help.ts +306 -306
- package/src/terminal/index.ts +71 -71
- package/src/terminal/output.ts +295 -295
- package/src/terminal/palette.ts +30 -30
- package/src/terminal/progress.ts +327 -327
- package/src/terminal/stream-writer.ts +214 -214
- package/src/terminal/table.ts +354 -354
- package/src/terminal/theme.ts +142 -142
- package/src/util/bun.ts +6 -6
- package/src/util/fs.ts +23 -23
- package/src/util/handlers.ts +96 -0
- package/src/util/manifest.ts +52 -52
- package/src/util/output.ts +22 -22
- package/src/util/port.ts +71 -71
- package/templates/default/AGENTS.md +96 -96
- package/templates/default/app/api/health/route.ts +13 -13
- package/templates/default/app/globals.css +49 -49
- package/templates/default/app/layout.tsx +27 -27
- package/templates/default/app/page.tsx +38 -38
- package/templates/default/package.json +1 -0
- package/templates/default/src/client/shared/lib/utils.ts +16 -16
- package/templates/default/src/client/shared/ui/button.tsx +57 -57
- package/templates/default/src/client/shared/ui/card.tsx +78 -78
- package/templates/default/src/client/shared/ui/index.ts +21 -21
- package/templates/default/src/client/shared/ui/input.tsx +24 -24
- package/templates/default/tests/example.test.ts +58 -58
- package/templates/default/tests/helpers.ts +52 -52
- package/templates/default/tests/setup.ts +9 -9
- package/templates/default/tsconfig.json +12 -14
- package/templates/default/apps/server/main.ts +0 -67
- package/templates/default/apps/web/entry.tsx +0 -35
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
6
|
-
import { cn } from "@/client/shared/lib/utils";
|
|
7
|
-
|
|
8
|
-
const buttonVariants = cva(
|
|
9
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
10
|
-
{
|
|
11
|
-
variants: {
|
|
12
|
-
variant: {
|
|
13
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
14
|
-
destructive:
|
|
15
|
-
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
16
|
-
outline:
|
|
17
|
-
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
18
|
-
secondary:
|
|
19
|
-
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
20
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
21
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
-
},
|
|
23
|
-
size: {
|
|
24
|
-
default: "h-10 px-4 py-2",
|
|
25
|
-
sm: "h-9 rounded-md px-3",
|
|
26
|
-
lg: "h-11 rounded-md px-8",
|
|
27
|
-
icon: "h-10 w-10",
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
defaultVariants: {
|
|
31
|
-
variant: "default",
|
|
32
|
-
size: "default",
|
|
33
|
-
},
|
|
34
|
-
}
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
export interface ButtonProps
|
|
38
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
|
-
VariantProps<typeof buttonVariants> {
|
|
40
|
-
asChild?: boolean;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
-
const Comp = asChild ? Slot : "button";
|
|
46
|
-
return (
|
|
47
|
-
<Comp
|
|
48
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
49
|
-
ref={ref}
|
|
50
|
-
{...props}
|
|
51
|
-
/>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
);
|
|
55
|
-
Button.displayName = "Button";
|
|
56
|
-
|
|
57
|
-
export { Button, buttonVariants };
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
6
|
+
import { cn } from "@/client/shared/lib/utils";
|
|
7
|
+
|
|
8
|
+
const buttonVariants = cva(
|
|
9
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
16
|
+
outline:
|
|
17
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
20
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
21
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: "h-10 px-4 py-2",
|
|
25
|
+
sm: "h-9 rounded-md px-3",
|
|
26
|
+
lg: "h-11 rounded-md px-8",
|
|
27
|
+
icon: "h-10 w-10",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: "default",
|
|
32
|
+
size: "default",
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
export interface ButtonProps
|
|
38
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
|
+
VariantProps<typeof buttonVariants> {
|
|
40
|
+
asChild?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
+
const Comp = asChild ? Slot : "button";
|
|
46
|
+
return (
|
|
47
|
+
<Comp
|
|
48
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
49
|
+
ref={ref}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
Button.displayName = "Button";
|
|
56
|
+
|
|
57
|
+
export { Button, buttonVariants };
|
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { cn } from "@/client/shared/lib/utils";
|
|
3
|
-
|
|
4
|
-
const Card = React.forwardRef<
|
|
5
|
-
HTMLDivElement,
|
|
6
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
7
|
-
>(({ className, ...props }, ref) => (
|
|
8
|
-
<div
|
|
9
|
-
ref={ref}
|
|
10
|
-
className={cn(
|
|
11
|
-
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
12
|
-
className
|
|
13
|
-
)}
|
|
14
|
-
{...props}
|
|
15
|
-
/>
|
|
16
|
-
));
|
|
17
|
-
Card.displayName = "Card";
|
|
18
|
-
|
|
19
|
-
const CardHeader = React.forwardRef<
|
|
20
|
-
HTMLDivElement,
|
|
21
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
22
|
-
>(({ className, ...props }, ref) => (
|
|
23
|
-
<div
|
|
24
|
-
ref={ref}
|
|
25
|
-
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
26
|
-
{...props}
|
|
27
|
-
/>
|
|
28
|
-
));
|
|
29
|
-
CardHeader.displayName = "CardHeader";
|
|
30
|
-
|
|
31
|
-
const CardTitle = React.forwardRef<
|
|
32
|
-
HTMLParagraphElement,
|
|
33
|
-
React.HTMLAttributes<HTMLHeadingElement>
|
|
34
|
-
>(({ className, ...props }, ref) => (
|
|
35
|
-
<h3
|
|
36
|
-
ref={ref}
|
|
37
|
-
className={cn(
|
|
38
|
-
"text-2xl font-semibold leading-none tracking-tight",
|
|
39
|
-
className
|
|
40
|
-
)}
|
|
41
|
-
{...props}
|
|
42
|
-
/>
|
|
43
|
-
));
|
|
44
|
-
CardTitle.displayName = "CardTitle";
|
|
45
|
-
|
|
46
|
-
const CardDescription = React.forwardRef<
|
|
47
|
-
HTMLParagraphElement,
|
|
48
|
-
React.HTMLAttributes<HTMLParagraphElement>
|
|
49
|
-
>(({ className, ...props }, ref) => (
|
|
50
|
-
<p
|
|
51
|
-
ref={ref}
|
|
52
|
-
className={cn("text-sm text-muted-foreground", className)}
|
|
53
|
-
{...props}
|
|
54
|
-
/>
|
|
55
|
-
));
|
|
56
|
-
CardDescription.displayName = "CardDescription";
|
|
57
|
-
|
|
58
|
-
const CardContent = React.forwardRef<
|
|
59
|
-
HTMLDivElement,
|
|
60
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
61
|
-
>(({ className, ...props }, ref) => (
|
|
62
|
-
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
63
|
-
));
|
|
64
|
-
CardContent.displayName = "CardContent";
|
|
65
|
-
|
|
66
|
-
const CardFooter = React.forwardRef<
|
|
67
|
-
HTMLDivElement,
|
|
68
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
69
|
-
>(({ className, ...props }, ref) => (
|
|
70
|
-
<div
|
|
71
|
-
ref={ref}
|
|
72
|
-
className={cn("flex items-center p-6 pt-0", className)}
|
|
73
|
-
{...props}
|
|
74
|
-
/>
|
|
75
|
-
));
|
|
76
|
-
CardFooter.displayName = "CardFooter";
|
|
77
|
-
|
|
78
|
-
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/client/shared/lib/utils";
|
|
3
|
+
|
|
4
|
+
const Card = React.forwardRef<
|
|
5
|
+
HTMLDivElement,
|
|
6
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
7
|
+
>(({ className, ...props }, ref) => (
|
|
8
|
+
<div
|
|
9
|
+
ref={ref}
|
|
10
|
+
className={cn(
|
|
11
|
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
));
|
|
17
|
+
Card.displayName = "Card";
|
|
18
|
+
|
|
19
|
+
const CardHeader = React.forwardRef<
|
|
20
|
+
HTMLDivElement,
|
|
21
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
22
|
+
>(({ className, ...props }, ref) => (
|
|
23
|
+
<div
|
|
24
|
+
ref={ref}
|
|
25
|
+
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
));
|
|
29
|
+
CardHeader.displayName = "CardHeader";
|
|
30
|
+
|
|
31
|
+
const CardTitle = React.forwardRef<
|
|
32
|
+
HTMLParagraphElement,
|
|
33
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
34
|
+
>(({ className, ...props }, ref) => (
|
|
35
|
+
<h3
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={cn(
|
|
38
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
39
|
+
className
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
));
|
|
44
|
+
CardTitle.displayName = "CardTitle";
|
|
45
|
+
|
|
46
|
+
const CardDescription = React.forwardRef<
|
|
47
|
+
HTMLParagraphElement,
|
|
48
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
49
|
+
>(({ className, ...props }, ref) => (
|
|
50
|
+
<p
|
|
51
|
+
ref={ref}
|
|
52
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
));
|
|
56
|
+
CardDescription.displayName = "CardDescription";
|
|
57
|
+
|
|
58
|
+
const CardContent = React.forwardRef<
|
|
59
|
+
HTMLDivElement,
|
|
60
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
61
|
+
>(({ className, ...props }, ref) => (
|
|
62
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
63
|
+
));
|
|
64
|
+
CardContent.displayName = "CardContent";
|
|
65
|
+
|
|
66
|
+
const CardFooter = React.forwardRef<
|
|
67
|
+
HTMLDivElement,
|
|
68
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
69
|
+
>(({ className, ...props }, ref) => (
|
|
70
|
+
<div
|
|
71
|
+
ref={ref}
|
|
72
|
+
className={cn("flex items-center p-6 pt-0", className)}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
));
|
|
76
|
+
CardFooter.displayName = "CardFooter";
|
|
77
|
+
|
|
78
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UI Components
|
|
3
|
-
*
|
|
4
|
-
* shadcn/ui 스타일의 컴포넌트 라이브러리
|
|
5
|
-
* Radix UI primitives + Tailwind CSS + cva 기반
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export { Button, buttonVariants } from "./button";
|
|
9
|
-
export type { ButtonProps } from "./button";
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
Card,
|
|
13
|
-
CardHeader,
|
|
14
|
-
CardFooter,
|
|
15
|
-
CardTitle,
|
|
16
|
-
CardDescription,
|
|
17
|
-
CardContent,
|
|
18
|
-
} from "./card";
|
|
19
|
-
|
|
20
|
-
export { Input } from "./input";
|
|
21
|
-
export type { InputProps } from "./input";
|
|
1
|
+
/**
|
|
2
|
+
* UI Components
|
|
3
|
+
*
|
|
4
|
+
* shadcn/ui 스타일의 컴포넌트 라이브러리
|
|
5
|
+
* Radix UI primitives + Tailwind CSS + cva 기반
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { Button, buttonVariants } from "./button";
|
|
9
|
+
export type { ButtonProps } from "./button";
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
Card,
|
|
13
|
+
CardHeader,
|
|
14
|
+
CardFooter,
|
|
15
|
+
CardTitle,
|
|
16
|
+
CardDescription,
|
|
17
|
+
CardContent,
|
|
18
|
+
} from "./card";
|
|
19
|
+
|
|
20
|
+
export { Input } from "./input";
|
|
21
|
+
export type { InputProps } from "./input";
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { cn } from "@/client/shared/lib/utils";
|
|
3
|
-
|
|
4
|
-
export interface InputProps
|
|
5
|
-
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
6
|
-
|
|
7
|
-
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
8
|
-
({ className, type, ...props }, ref) => {
|
|
9
|
-
return (
|
|
10
|
-
<input
|
|
11
|
-
type={type}
|
|
12
|
-
className={cn(
|
|
13
|
-
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
14
|
-
className
|
|
15
|
-
)}
|
|
16
|
-
ref={ref}
|
|
17
|
-
{...props}
|
|
18
|
-
/>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
);
|
|
22
|
-
Input.displayName = "Input";
|
|
23
|
-
|
|
24
|
-
export { Input };
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/client/shared/lib/utils";
|
|
3
|
+
|
|
4
|
+
export interface InputProps
|
|
5
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
6
|
+
|
|
7
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
8
|
+
({ className, type, ...props }, ref) => {
|
|
9
|
+
return (
|
|
10
|
+
<input
|
|
11
|
+
type={type}
|
|
12
|
+
className={cn(
|
|
13
|
+
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
ref={ref}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
Input.displayName = "Input";
|
|
23
|
+
|
|
24
|
+
export { Input };
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
// Mandu Example Test
|
|
2
|
-
// 이 파일은 테스트 작성 방법을 보여주는 예제입니다.
|
|
3
|
-
|
|
4
|
-
import { describe, it, expect } from "bun:test";
|
|
5
|
-
import { createTestRequest, parseJsonResponse, assertStatus } from "./helpers";
|
|
6
|
-
|
|
7
|
-
describe("Example Tests", () => {
|
|
8
|
-
describe("Basic Assertions", () => {
|
|
9
|
-
it("should pass basic equality test", () => {
|
|
10
|
-
expect(1 + 1).toBe(2);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("should pass object equality test", () => {
|
|
14
|
-
const obj = { status: "ok", data: { message: "hello" } };
|
|
15
|
-
expect(obj).toEqual({
|
|
16
|
-
status: "ok",
|
|
17
|
-
data: { message: "hello" },
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe("Test Helpers", () => {
|
|
23
|
-
it("should create test request", () => {
|
|
24
|
-
const req = createTestRequest("http://localhost:3000/api/test", {
|
|
25
|
-
method: "POST",
|
|
26
|
-
body: { name: "test" },
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
expect(req.method).toBe("POST");
|
|
30
|
-
expect(req.url).toBe("http://localhost:3000/api/test");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("should parse JSON response", async () => {
|
|
34
|
-
const mockResponse = new Response(
|
|
35
|
-
JSON.stringify({ status: "ok" }),
|
|
36
|
-
{ status: 200 }
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const data = await parseJsonResponse<{ status: string }>(mockResponse);
|
|
40
|
-
expect(data.status).toBe("ok");
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// API 핸들러 테스트 예제 (실제 핸들러 import 후 사용)
|
|
46
|
-
// import handler from "
|
|
47
|
-
//
|
|
48
|
-
// describe("API: GET /api/health", () => {
|
|
49
|
-
// it("should return 200 with status ok", async () => {
|
|
50
|
-
// const req = createTestRequest("http://localhost:3000/api/health");
|
|
51
|
-
// const response = handler(req, {});
|
|
52
|
-
//
|
|
53
|
-
// assertStatus(response, 200);
|
|
54
|
-
//
|
|
55
|
-
// const data = await parseJsonResponse<{ status: string }>(response);
|
|
56
|
-
// expect(data.status).toBe("ok");
|
|
57
|
-
// });
|
|
58
|
-
// });
|
|
1
|
+
// Mandu Example Test
|
|
2
|
+
// 이 파일은 테스트 작성 방법을 보여주는 예제입니다.
|
|
3
|
+
|
|
4
|
+
import { describe, it, expect } from "bun:test";
|
|
5
|
+
import { createTestRequest, parseJsonResponse, assertStatus } from "./helpers";
|
|
6
|
+
|
|
7
|
+
describe("Example Tests", () => {
|
|
8
|
+
describe("Basic Assertions", () => {
|
|
9
|
+
it("should pass basic equality test", () => {
|
|
10
|
+
expect(1 + 1).toBe(2);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should pass object equality test", () => {
|
|
14
|
+
const obj = { status: "ok", data: { message: "hello" } };
|
|
15
|
+
expect(obj).toEqual({
|
|
16
|
+
status: "ok",
|
|
17
|
+
data: { message: "hello" },
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("Test Helpers", () => {
|
|
23
|
+
it("should create test request", () => {
|
|
24
|
+
const req = createTestRequest("http://localhost:3000/api/test", {
|
|
25
|
+
method: "POST",
|
|
26
|
+
body: { name: "test" },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(req.method).toBe("POST");
|
|
30
|
+
expect(req.url).toBe("http://localhost:3000/api/test");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should parse JSON response", async () => {
|
|
34
|
+
const mockResponse = new Response(
|
|
35
|
+
JSON.stringify({ status: "ok" }),
|
|
36
|
+
{ status: 200 }
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const data = await parseJsonResponse<{ status: string }>(mockResponse);
|
|
40
|
+
expect(data.status).toBe("ok");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// API 핸들러 테스트 예제 (실제 핸들러 import 후 사용)
|
|
46
|
+
// import handler from "../.mandu/generated/server/routes/health.route";
|
|
47
|
+
//
|
|
48
|
+
// describe("API: GET /api/health", () => {
|
|
49
|
+
// it("should return 200 with status ok", async () => {
|
|
50
|
+
// const req = createTestRequest("http://localhost:3000/api/health");
|
|
51
|
+
// const response = handler(req, {});
|
|
52
|
+
//
|
|
53
|
+
// assertStatus(response, 200);
|
|
54
|
+
//
|
|
55
|
+
// const data = await parseJsonResponse<{ status: string }>(response);
|
|
56
|
+
// expect(data.status).toBe("ok");
|
|
57
|
+
// });
|
|
58
|
+
// });
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
// Mandu Test Helpers
|
|
2
|
-
// 테스트에서 사용할 유틸리티 함수들
|
|
3
|
-
|
|
4
|
-
import type { Request } from "bun";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* API 핸들러 테스트용 Request 생성
|
|
8
|
-
*/
|
|
9
|
-
export function createTestRequest(
|
|
10
|
-
url: string,
|
|
11
|
-
options?: {
|
|
12
|
-
method?: string;
|
|
13
|
-
body?: unknown;
|
|
14
|
-
headers?: Record<string, string>;
|
|
15
|
-
}
|
|
16
|
-
): Request {
|
|
17
|
-
const { method = "GET", body, headers = {} } = options || {};
|
|
18
|
-
|
|
19
|
-
return new Request(url, {
|
|
20
|
-
method,
|
|
21
|
-
headers: {
|
|
22
|
-
"Content-Type": "application/json",
|
|
23
|
-
...headers,
|
|
24
|
-
},
|
|
25
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Response를 JSON으로 파싱
|
|
31
|
-
*/
|
|
32
|
-
export async function parseJsonResponse<T = unknown>(response: Response): Promise<T> {
|
|
33
|
-
return response.json() as Promise<T>;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Response 상태 검증
|
|
38
|
-
*/
|
|
39
|
-
export function assertStatus(response: Response, expectedStatus: number): void {
|
|
40
|
-
if (response.status !== expectedStatus) {
|
|
41
|
-
throw new Error(
|
|
42
|
-
`Expected status ${expectedStatus}, got ${response.status}`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* 테스트용 라우트 파라미터 생성
|
|
49
|
-
*/
|
|
50
|
-
export function createParams(params: Record<string, string>): Record<string, string> {
|
|
51
|
-
return params;
|
|
52
|
-
}
|
|
1
|
+
// Mandu Test Helpers
|
|
2
|
+
// 테스트에서 사용할 유틸리티 함수들
|
|
3
|
+
|
|
4
|
+
import type { Request } from "bun";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* API 핸들러 테스트용 Request 생성
|
|
8
|
+
*/
|
|
9
|
+
export function createTestRequest(
|
|
10
|
+
url: string,
|
|
11
|
+
options?: {
|
|
12
|
+
method?: string;
|
|
13
|
+
body?: unknown;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
): Request {
|
|
17
|
+
const { method = "GET", body, headers = {} } = options || {};
|
|
18
|
+
|
|
19
|
+
return new Request(url, {
|
|
20
|
+
method,
|
|
21
|
+
headers: {
|
|
22
|
+
"Content-Type": "application/json",
|
|
23
|
+
...headers,
|
|
24
|
+
},
|
|
25
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Response를 JSON으로 파싱
|
|
31
|
+
*/
|
|
32
|
+
export async function parseJsonResponse<T = unknown>(response: Response): Promise<T> {
|
|
33
|
+
return response.json() as Promise<T>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Response 상태 검증
|
|
38
|
+
*/
|
|
39
|
+
export function assertStatus(response: Response, expectedStatus: number): void {
|
|
40
|
+
if (response.status !== expectedStatus) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Expected status ${expectedStatus}, got ${response.status}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 테스트용 라우트 파라미터 생성
|
|
49
|
+
*/
|
|
50
|
+
export function createParams(params: Record<string, string>): Record<string, string> {
|
|
51
|
+
return params;
|
|
52
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// Mandu Test Setup
|
|
2
|
-
// Bun 테스트 환경 설정
|
|
3
|
-
|
|
4
|
-
// 테스트 타임아웃 설정 (필요 시)
|
|
5
|
-
// import { setDefaultTimeout } from "bun:test";
|
|
6
|
-
// setDefaultTimeout(10000);
|
|
7
|
-
|
|
8
|
-
// 환경 변수 설정
|
|
9
|
-
process.env.NODE_ENV = "test";
|
|
1
|
+
// Mandu Test Setup
|
|
2
|
+
// Bun 테스트 환경 설정
|
|
3
|
+
|
|
4
|
+
// 테스트 타임아웃 설정 (필요 시)
|
|
5
|
+
// import { setDefaultTimeout } from "bun:test";
|
|
6
|
+
// setDefaultTimeout(10000);
|
|
7
|
+
|
|
8
|
+
// 환경 변수 설정
|
|
9
|
+
process.env.NODE_ENV = "test";
|
|
@@ -7,19 +7,17 @@
|
|
|
7
7
|
"strict": true,
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"jsx": "react-jsx",
|
|
10
|
-
"types": ["bun-types"],
|
|
11
|
-
"baseUrl": ".",
|
|
12
|
-
"paths": {
|
|
13
|
-
"@/*": ["./src/*"]
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"include": [
|
|
17
|
-
"app/**/*.ts",
|
|
18
|
-
"app/**/*.tsx",
|
|
19
|
-
"src/**/*.ts",
|
|
20
|
-
"src/**/*.tsx"
|
|
21
|
-
|
|
22
|
-
"apps/**/*.tsx"
|
|
23
|
-
],
|
|
10
|
+
"types": ["bun-types"],
|
|
11
|
+
"baseUrl": ".",
|
|
12
|
+
"paths": {
|
|
13
|
+
"@/*": ["./src/*"]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"include": [
|
|
17
|
+
"app/**/*.ts",
|
|
18
|
+
"app/**/*.tsx",
|
|
19
|
+
"src/**/*.ts",
|
|
20
|
+
"src/**/*.tsx"
|
|
21
|
+
],
|
|
24
22
|
"exclude": ["node_modules"]
|
|
25
23
|
}
|