From 19ffd65971f9b2d3dcefbd08423c490c66109088 Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Fri, 22 May 2026 18:59:37 -0300 Subject: [PATCH] Move all tests under test/ mirroring src layout. Centralize Vitest discovery, add @test path alias, and include test files in typecheck and lint. --- eslint.config.js | 2 +- {src => test}/cli/commit-errors.test.ts | 0 {src => test}/cli/commit.test.ts | 2 +- {src => test}/cli/doctor.test.ts | 2 +- {src => test}/cli/effort.test.ts | 4 ++-- {src => test}/cli/model.test.ts | 4 ++-- {src => test}/cli/parser.test.ts | 7 ++++--- {src => test}/cli/setup.test.ts | 4 ++-- test/{cli-smoke.test.ts => cli/smoke.test.ts} | 2 +- {src => test}/cli/update.test.ts | 2 +- {src => test}/domain/commit/prompts.test.ts | 0 {src => test}/domain/config/config.test.ts | 9 +++++---- {src => test}/domain/llm/auth-resolver.test.ts | 6 +++--- {src => test}/domain/llm/response-parser.test.ts | 2 +- {src => test}/domain/llm/router.test.ts | 15 ++++++++------- {src => test}/infra/git/parsers.test.ts | 2 +- {src => test}/infra/git/repo.integration.test.ts | 4 ++-- {src => test}/infra/github/pr.test.ts | 2 +- {src => test}/infra/storage/config.test.ts | 2 +- {src => test}/infra/version-check.test.ts | 0 {src => test}/libs/fuzzy.test.ts | 0 tsconfig.json | 7 ++++--- vitest.config.ts | 3 ++- 23 files changed, 43 insertions(+), 38 deletions(-) rename {src => test}/cli/commit-errors.test.ts (100%) rename {src => test}/cli/commit.test.ts (97%) rename {src => test}/cli/doctor.test.ts (95%) rename {src => test}/cli/effort.test.ts (92%) rename {src => test}/cli/model.test.ts (94%) rename {src => test}/cli/parser.test.ts (73%) rename {src => test}/cli/setup.test.ts (94%) rename test/{cli-smoke.test.ts => cli/smoke.test.ts} (95%) rename {src => test}/cli/update.test.ts (94%) rename {src => test}/domain/commit/prompts.test.ts (100%) rename {src => test}/domain/config/config.test.ts (78%) rename {src => test}/domain/llm/auth-resolver.test.ts (93%) rename {src => test}/domain/llm/response-parser.test.ts (91%) rename {src => test}/domain/llm/router.test.ts (88%) rename {src => test}/infra/git/parsers.test.ts (96%) rename {src => test}/infra/git/repo.integration.test.ts (89%) rename {src => test}/infra/github/pr.test.ts (96%) rename {src => test}/infra/storage/config.test.ts (96%) rename {src => test}/infra/version-check.test.ts (100%) rename {src => test}/libs/fuzzy.test.ts (100%) diff --git a/eslint.config.js b/eslint.config.js index f6817c5..500c0be 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -4,7 +4,7 @@ import sonarjs from "eslint-plugin-sonarjs"; export default tseslint.config( { ignores: ["dist/**", "node_modules/**", "**/*.d.ts"] }, { - files: ["index.ts", "src/**/*.{ts,tsx}"], + files: ["index.ts", "src/**/*.{ts,tsx}", "test/**/*.ts"], languageOptions: { parser: tseslint.parser }, plugins: { sonarjs }, rules: { diff --git a/src/cli/commit-errors.test.ts b/test/cli/commit-errors.test.ts similarity index 100% rename from src/cli/commit-errors.test.ts rename to test/cli/commit-errors.test.ts diff --git a/src/cli/commit.test.ts b/test/cli/commit.test.ts similarity index 97% rename from src/cli/commit.test.ts rename to test/cli/commit.test.ts index dcead02..712146e 100644 --- a/src/cli/commit.test.ts +++ b/test/cli/commit.test.ts @@ -7,7 +7,7 @@ vi.mock("@/infra/env", () => ({ import { Commit } from "@/cli/commit"; import { Future } from "@/libs/future"; import { Nothing, Just } from "@/libs/maybe"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; import * as s from "@/libs/json/schema"; import { Config } from "@/domain/config/config"; diff --git a/src/cli/doctor.test.ts b/test/cli/doctor.test.ts similarity index 95% rename from src/cli/doctor.test.ts rename to test/cli/doctor.test.ts index f533e63..234d1f5 100644 --- a/src/cli/doctor.test.ts +++ b/test/cli/doctor.test.ts @@ -7,7 +7,7 @@ vi.mock("@/infra/env", () => ({ import { Doctor } from "@/cli/doctor"; import { Future } from "@/libs/future"; import { Just } from "@/libs/maybe"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; vi.mock("@/infra/storage/config", async (importOriginal) => { const actual = await importOriginal(); diff --git a/src/cli/effort.test.ts b/test/cli/effort.test.ts similarity index 92% rename from src/cli/effort.test.ts rename to test/cli/effort.test.ts index 5ce20f3..b9b668e 100644 --- a/src/cli/effort.test.ts +++ b/test/cli/effort.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; import { EffortCommand } from "@/cli/effort"; import { Future } from "@/libs/future"; import { Nothing, Just } from "@/libs/maybe"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; import * as s from "@/libs/json/schema"; import { Config } from "@/domain/config/config"; @@ -10,7 +10,7 @@ type ConfigValue = s.Infer; vi.mock("@/infra/storage/config", () => ({ loadConfig: vi.fn(), - saveConfig: vi.fn(() => Future.resolve()) + saveConfig: vi.fn(() => Future.resolve(undefined)) })); vi.mock("@/infra/ui/effort-picker", () => ({ selectOpenAIEffort: vi.fn(() => Future.resolve(Just("low"))) diff --git a/src/cli/model.test.ts b/test/cli/model.test.ts similarity index 94% rename from src/cli/model.test.ts rename to test/cli/model.test.ts index f3bdac7..8e948b9 100644 --- a/src/cli/model.test.ts +++ b/test/cli/model.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; import { ModelCommand } from "@/cli/model"; import { Future } from "@/libs/future"; import { Nothing, Just } from "@/libs/maybe"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; import * as s from "@/libs/json/schema"; import { Config } from "@/domain/config/config"; @@ -16,7 +16,7 @@ const config = (): ConfigValue => ({ vi.mock("@/infra/storage/config", () => ({ loadConfig: vi.fn(), - saveConfig: vi.fn(() => Future.resolve()) + saveConfig: vi.fn(() => Future.resolve(undefined)) })); vi.mock("@/domain/llm/auth-resolver", () => ({ resolveProvider: vi.fn((c: ConfigValue) => Future.resolve(c.ai)) diff --git a/src/cli/parser.test.ts b/test/cli/parser.test.ts similarity index 73% rename from src/cli/parser.test.ts rename to test/cli/parser.test.ts index 131dda2..a67d2dc 100644 --- a/src/cli/parser.test.ts +++ b/test/cli/parser.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { parseArgs } from "@/cli/parser"; +import { Failure, Success } from "@/libs/result"; describe("parseArgs", () => { it.each([ @@ -17,18 +18,18 @@ describe("parseArgs", () => { ] as const)("maps %j to %s", (argv, type) => { const result = parseArgs([...argv]); expect(result.isSuccess()).toBe(true); - if (result.isSuccess()) expect(result.value.type).toBe(type); + if (result instanceof Success) expect(result.value.type).toBe(type); }); it("rejects unknown commands", () => { const result = parseArgs(["wat"]); expect(result.isFailure()).toBe(true); - if (result.isFailure()) expect(result.error.message).toContain("Unknown command"); + if (result instanceof Failure) expect(result.error.message).toContain("Unknown command"); }); it("defaults bare invocation to generate", () => { const result = parseArgs([]); expect(result.isSuccess()).toBe(true); - if (result.isSuccess()) expect(result.value.type).toBe("generate"); + if (result instanceof Success) expect(result.value.type).toBe("generate"); }); }); diff --git a/src/cli/setup.test.ts b/test/cli/setup.test.ts similarity index 94% rename from src/cli/setup.test.ts rename to test/cli/setup.test.ts index f301d4c..02c7cf9 100644 --- a/src/cli/setup.test.ts +++ b/test/cli/setup.test.ts @@ -14,7 +14,7 @@ vi.mock("@/infra/auth/openai", () => ({ import { Setup } from "@/cli/setup"; import { Future } from "@/libs/future"; import { Just } from "@/libs/maybe"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; vi.mock("@clack/prompts", () => ({ intro: vi.fn(), @@ -40,7 +40,7 @@ vi.mock("@/infra/ui/spinner", () => ({ bracketStatus: vi.fn((_a: string, _b: string, f: (s: unknown) => Future) => f({})) })); vi.mock("@/infra/storage/config", () => ({ - saveConfig: vi.fn(() => Future.resolve()) + saveConfig: vi.fn(() => Future.resolve(undefined)) })); vi.mock("@/infra/auth/anthropic", () => ({ validateAnthropicApiKey: vi.fn(), diff --git a/test/cli-smoke.test.ts b/test/cli/smoke.test.ts similarity index 95% rename from test/cli-smoke.test.ts rename to test/cli/smoke.test.ts index 95c5b27..87a5ce8 100644 --- a/test/cli-smoke.test.ts +++ b/test/cli/smoke.test.ts @@ -3,7 +3,7 @@ import { existsSync } from "node:fs"; import { resolve } from "node:path"; import { spawnSync } from "node:child_process"; -const BIN = resolve(import.meta.dirname, "../dist/index.js"); +const BIN = resolve(import.meta.dirname, "../../dist/index.js"); const runCli = (args: string[]) => spawnSync(process.execPath, [BIN, ...args], { diff --git a/src/cli/update.test.ts b/test/cli/update.test.ts similarity index 94% rename from src/cli/update.test.ts rename to test/cli/update.test.ts index 8438f87..1b961f5 100644 --- a/src/cli/update.test.ts +++ b/test/cli/update.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest"; import { detectPackageManager } from "@/cli/update"; import { Future } from "@/libs/future"; import { Success } from "@/libs/result"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; vi.mock("@/infra/shell", () => ({ execBin: vi.fn() diff --git a/src/domain/commit/prompts.test.ts b/test/domain/commit/prompts.test.ts similarity index 100% rename from src/domain/commit/prompts.test.ts rename to test/domain/commit/prompts.test.ts diff --git a/src/domain/config/config.test.ts b/test/domain/config/config.test.ts similarity index 78% rename from src/domain/config/config.test.ts rename to test/domain/config/config.test.ts index 51c7c0f..ef0640a 100644 --- a/src/domain/config/config.test.ts +++ b/test/domain/config/config.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import * as s from "@/libs/json/schema"; import { Config } from "@/domain/config/config"; import { Just, Nothing } from "@/libs/maybe"; +import { Success } from "@/libs/result"; type ConfigValue = s.Infer; @@ -21,10 +22,10 @@ describe("Config schema", () => { const encoded = s.encode(Config, sampleConfig()); const decoded = s.decode(Config, encoded); expect(decoded.isSuccess()).toBe(true); - if (!decoded.isSuccess()) return; + if (!(decoded instanceof Success)) return; const again = s.decode(Config, s.encode(Config, decoded.value)); expect(again.isSuccess()).toBe(true); - if (again.isSuccess()) { + if (again instanceof Success) { expect(again.value.ai.provider).toBe("openai"); expect(again.value.custom_template).toBeInstanceOf(Nothing); } @@ -37,11 +38,11 @@ describe("Config schema", () => { it("accepts custom convention with template", () => { const json = { - ...s.encode(Config, { ...sampleConfig(), commit_convention: "custom", custom_template: Just("Summarize:\n{diff}") }), + ...(s.encode(Config, { ...sampleConfig(), commit_convention: "custom", custom_template: Just("Summarize:\n{diff}") }) as Record), commit_convention: "custom" }; const result = s.decode(Config, json); expect(result.isSuccess()).toBe(true); - if (result.isSuccess()) expect(result.value.custom_template).toBeInstanceOf(Just); + if (result instanceof Success) expect(result.value.custom_template).toBeInstanceOf(Just); }); }); diff --git a/src/domain/llm/auth-resolver.test.ts b/test/domain/llm/auth-resolver.test.ts similarity index 93% rename from src/domain/llm/auth-resolver.test.ts rename to test/domain/llm/auth-resolver.test.ts index 8a1b662..0964617 100644 --- a/src/domain/llm/auth-resolver.test.ts +++ b/test/domain/llm/auth-resolver.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; import { Future } from "@/libs/future"; import { resolveProvider, tokensChanged } from "@/domain/llm/auth-resolver"; import { Just, Nothing } from "@/libs/maybe"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; import * as s from "@/libs/json/schema"; import { Config as ConfigSchema } from "@/domain/config/config"; @@ -11,8 +11,8 @@ vi.mock("@/infra/auth/google", () => ({ })); vi.mock("@/infra/auth/openai", () => ({ ensureFreshOpenAITokens: vi.fn() })); vi.mock("@/infra/storage/config", () => ({ - updateGoogleTokens: vi.fn(() => Future.resolve()), - updateOpenAITokens: vi.fn(() => Future.resolve()) + updateGoogleTokens: vi.fn(() => Future.resolve(undefined)), + updateOpenAITokens: vi.fn(() => Future.resolve(undefined)) })); type ConfigValue = s.Infer; diff --git a/src/domain/llm/response-parser.test.ts b/test/domain/llm/response-parser.test.ts similarity index 91% rename from src/domain/llm/response-parser.test.ts rename to test/domain/llm/response-parser.test.ts index ef35bf8..9d5aa48 100644 --- a/src/domain/llm/response-parser.test.ts +++ b/test/domain/llm/response-parser.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { extractResponse } from "@/domain/llm/response-parser"; import { Just, Nothing } from "@/libs/maybe"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; describe("extractResponse", () => { it("trims and returns non-empty text", async () => { diff --git a/src/domain/llm/router.test.ts b/test/domain/llm/router.test.ts similarity index 88% rename from src/domain/llm/router.test.ts rename to test/domain/llm/router.test.ts index 6027000..3af16c8 100644 --- a/src/domain/llm/router.test.ts +++ b/test/domain/llm/router.test.ts @@ -2,15 +2,16 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; import { generateCommitMessage, refineCommitMessage } from "@/domain/llm/router"; import { Future } from "@/libs/future"; import { Nothing } from "@/libs/maybe"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; import type { ProviderConfig } from "@/domain/config/config"; -const mockProvider = (provider: ProviderConfig["provider"]): ProviderConfig => ({ - provider, - model: "test-model", - effort: Nothing(), - auth_method: { type: "api_key", content: "sk-test" } -}); +const mockProvider = (provider: ProviderConfig["provider"]): ProviderConfig => + ({ + provider, + model: "test-model", + effort: Nothing(), + auth_method: { type: "api_key", content: "sk-test" } + }) as ProviderConfig; vi.mock("@/infra/llm/gemini", () => ({ generateContentWithGemini: vi.fn(() => Future.resolve({ text: "feat: test", tokens: Nothing() })) diff --git a/src/infra/git/parsers.test.ts b/test/infra/git/parsers.test.ts similarity index 96% rename from src/infra/git/parsers.test.ts rename to test/infra/git/parsers.test.ts index ff3aa9f..c56a172 100644 --- a/src/infra/git/parsers.test.ts +++ b/test/infra/git/parsers.test.ts @@ -42,7 +42,7 @@ describe("splitCommitFields", () => { const line = ["abc", "abc", "subject", "me", "me@x.com", "2024-01-01T00:00:00+00:00"].join("\0"); const result = splitCommitFields(`${line}\n`); expect(result.isSuccess()).toBe(true); - if (result.isSuccess()) expect(result.value["subject"]).toBe("subject"); + if (result instanceof Success) expect(result.value["subject"]).toBe("subject"); }); }); diff --git a/src/infra/git/repo.integration.test.ts b/test/infra/git/repo.integration.test.ts similarity index 89% rename from src/infra/git/repo.integration.test.ts rename to test/infra/git/repo.integration.test.ts index 1ee858b..3eb790c 100644 --- a/src/infra/git/repo.integration.test.ts +++ b/test/infra/git/repo.integration.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { chdir, cwd } from "node:process"; -import { runFuture } from "../../../test/helpers/run-future"; -import { createTempGitRepo } from "../../../test/helpers/temp-git-repo"; +import { runFuture } from "@test/helpers/run-future"; +import { createTempGitRepo } from "@test/helpers/temp-git-repo"; import * as repo from "@/infra/git/repo"; describe("git repo integration", () => { diff --git a/src/infra/github/pr.test.ts b/test/infra/github/pr.test.ts similarity index 96% rename from src/infra/github/pr.test.ts rename to test/infra/github/pr.test.ts index 9efedb7..56dfdb0 100644 --- a/src/infra/github/pr.test.ts +++ b/test/infra/github/pr.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; import { getOpenPullRequest } from "@/infra/github/pr"; import { Future } from "@/libs/future"; import { Success, Failure } from "@/libs/result"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; vi.mock("@/infra/git/repo", () => ({ getTrackingRemoteUrl: vi.fn(() => Future.resolve("https://github.com/o/r.git")), diff --git a/src/infra/storage/config.test.ts b/test/infra/storage/config.test.ts similarity index 96% rename from src/infra/storage/config.test.ts rename to test/infra/storage/config.test.ts index 43da65e..4c9e29c 100644 --- a/src/infra/storage/config.test.ts +++ b/test/infra/storage/config.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, beforeEach } from "vitest"; import { readFile } from "node:fs/promises"; import { loadConfig, saveConfig, configFile } from "@/infra/storage/config"; import { Nothing } from "@/libs/maybe"; -import { runFuture } from "../../../test/helpers/run-future"; +import { runFuture } from "@test/helpers/run-future"; import * as s from "@/libs/json/schema"; import { Config } from "@/domain/config/config"; diff --git a/src/infra/version-check.test.ts b/test/infra/version-check.test.ts similarity index 100% rename from src/infra/version-check.test.ts rename to test/infra/version-check.test.ts diff --git a/src/libs/fuzzy.test.ts b/test/libs/fuzzy.test.ts similarity index 100% rename from src/libs/fuzzy.test.ts rename to test/libs/fuzzy.test.ts diff --git a/tsconfig.json b/tsconfig.json index ebfb76e..6ff9ab4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,9 +25,10 @@ "allowUnusedLabels": false, "allowUnreachableCode": false, "paths": { - "@/*": ["./src/*", "./*"] + "@/*": ["./src/*", "./*"], + "@test/*": ["./test/*"] } }, - "include": ["index.ts", "src/**/*"], - "exclude": ["node_modules", "dist", "**/*.test.ts", "test"] + "include": ["index.ts", "src/**/*", "test/**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/vitest.config.ts b/vitest.config.ts index 4f75cf4..9a7ed5a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,12 +7,13 @@ export default defineConfig({ resolve: { alias: [ { find: "@/package.json", replacement: resolve(root, "package.json") }, + { find: /^@test\/(.+)$/, replacement: resolve(root, "test/$1") }, { find: /^@\/(.+)$/, replacement: resolve(root, "src/$1") } ] }, test: { environment: "node", - include: ["src/**/*.test.ts", "test/**/*.test.ts"], + include: ["test/**/*.test.ts"], setupFiles: ["./test/setup.ts"], coverage: { provider: "v8",