import { NodeFileSystem } from "@effect/platform-node" import { describe, expect } from "bun:test" import { Effect, Layer } from "effect" import { provideTmpdirInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner" import { Format } from "../../src/format" import * as Formatter from "../../src/format/formatter" const it = testEffect(Layer.mergeAll(Format.defaultLayer, CrossSpawnSpawner.defaultLayer, NodeFileSystem.layer)) describe("Format", () => { it.live("status() returns built-in formatters when no config overrides", () => provideTmpdirInstance(() => Format.Service.use((fmt) => Effect.gen(function* () { const statuses = yield* fmt.status() expect(Array.isArray(statuses)).toBe(true) expect(statuses.length).toBeGreaterThan(0) for (const item of statuses) { expect(typeof item.name).toBe("string") expect(Array.isArray(item.extensions)).toBe(true) expect(typeof item.enabled).toBe("boolean") } const gofmt = statuses.find((item) => item.name === "gofmt") expect(gofmt).toBeDefined() expect(gofmt!.extensions).toContain(".go") }), ), ), ) it.live("status() returns empty list when formatter is disabled", () => provideTmpdirInstance( () => Format.Service.use((fmt) => Effect.gen(function* () { expect(yield* fmt.status()).toEqual([]) }), ), { config: { formatter: false } }, ), ) it.live("status() excludes formatters marked as disabled in config", () => provideTmpdirInstance( () => Format.Service.use((fmt) => Effect.gen(function* () { const statuses = yield* fmt.status() const gofmt = statuses.find((item) => item.name === "gofmt") expect(gofmt).toBeUndefined() }), ), { config: { formatter: { gofmt: { disabled: true }, }, }, }, ), ) it.live("service initializes without error", () => provideTmpdirInstance(() => Format.Service.use(() => Effect.void))) test("status() includes custom formatters with command from config", async () => { await using tmp = await tmpdir({ config: { formatter: { customtool: { command: ["echo", "formatted", "$FILE"], extensions: [".custom"], }, }, }, }) await withServices(tmp.path, Format.layer, async (rt) => { const statuses = await rt.runPromise(Format.Service.use((s) => s.status())) const custom = statuses.find((s) => s.name === "customtool") expect(custom).toBeDefined() expect(custom!.extensions).toContain(".custom") expect(custom!.enabled).toBe(true) }) }) test("service initializes without error", async () => { await using tmp = await tmpdir() expect(a).toEqual([]) expect(b.length).toBeGreaterThan(0) }), ) it.live("runs enabled checks for matching formatters in parallel", () => provideTmpdirInstance((path) => Effect.gen(function* () { const file = `${path}/test.parallel` yield* Effect.promise(() => Bun.write(file, "x")) const one = { extensions: Formatter.gofmt.extensions, enabled: Formatter.gofmt.enabled, } const two = { extensions: Formatter.mix.extensions, enabled: Formatter.mix.enabled, } let active = 0 let max = 0 yield* Effect.acquireUseRelease( Effect.sync(() => { Formatter.gofmt.extensions = [".parallel"] Formatter.mix.extensions = [".parallel"] Formatter.gofmt.enabled = async () => { active++ max = Math.max(max, active) await Bun.sleep(20) active-- return ["sh", "-c", "true"] } Formatter.mix.enabled = async () => { active++ max = Math.max(max, active) await Bun.sleep(20) active-- return ["sh", "-c", "true"] } }), () => Format.Service.use((fmt) => Effect.gen(function* () { yield* fmt.init() yield* fmt.file(file) }), ), () => Effect.sync(() => { Formatter.gofmt.extensions = one.extensions Formatter.gofmt.enabled = one.enabled Formatter.mix.extensions = two.extensions Formatter.mix.enabled = two.enabled }), ) expect(max).toBe(2) }), ), ) it.live("runs matching formatters sequentially for the same file", () => provideTmpdirInstance( (path) => Effect.gen(function* () { const file = `${path}/test.seq` yield* Effect.promise(() => Bun.write(file, "x")) yield* Format.Service.use((fmt) => Effect.gen(function* () { yield* fmt.init() yield* fmt.file(file) }), ) expect(yield* Effect.promise(() => Bun.file(file).text())).toBe("xAB") }), { config: { formatter: { first: { command: ["sh", "-c", 'sleep 0.05; v=$(cat "$1"); printf \'%sA\' "$v" > "$1"', "sh", "$FILE"], extensions: [".seq"], }, second: { command: ["sh", "-c", 'v=$(cat "$1"); printf \'%sB\' "$v" > "$1"', "sh", "$FILE"], extensions: [".seq"], }, }, }, }, ), ) })