Files
Kit Langton 84fd347afa
deploy-www / deploy (push) Has been cancelled
publish / version (push) Has been cancelled
publish / build-cli (push) Has been cancelled
publish / build-node-cli (map[host:blacksmith-4vcpu-ubuntu-2404 target:linux-x64]) (push) Has been cancelled
publish / build-node-cli (map[host:blacksmith-4vcpu-ubuntu-2404-arm target:linux-arm64]) (push) Has been cancelled
publish / build-node-cli (map[host:blacksmith-4vcpu-windows-2025 target:windows-arm64]) (push) Has been cancelled
publish / build-node-cli (map[host:blacksmith-4vcpu-windows-2025 target:windows-x64]) (push) Has been cancelled
publish / build-node-cli (map[host:macos-26 target:darwin-arm64]) (push) Has been cancelled
publish / sign-cli-windows (push) Has been cancelled
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Has been cancelled
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Has been cancelled
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Has been cancelled
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404-arm platform_flag:--linux --arm64 target:aarch64-unknown-linux-gnu]) (push) Has been cancelled
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Has been cancelled
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Has been cancelled
publish / publish (push) Has been cancelled
generate / generate (push) Has been cancelled
typecheck / typecheck (push) Has been cancelled
fix(codegen): write prettier-stable generated manifests (#41343)
2026-08-08 20:52:04 -04:00

161 lines
4.9 KiB
TypeScript

import { describe, expect } from "bun:test"
import { Effect, FileSystem, Option } from "effect"
import { write, type Output } from "../src"
import { it } from "./effect"
describe("HttpApiCodegen.write", () => {
it.effect("writes compiled files beneath the output directory", () => {
const writes: Array<{ readonly path: string; readonly content: string }> = []
const output: Output = {
operations: [],
files: [{ path: "session.ts", content: "export const session = {}" }],
}
return Effect.gen(function* () {
yield* write(output, "/generated")
expect(writes).toEqual([
{ path: "/generated/session.ts", content: "export const session = {}\n" },
{ path: "/generated/.httpapi-codegen.json", content: '["session.ts"]\n' },
])
}).pipe(
Effect.provideService(
FileSystem.FileSystem,
FileSystem.makeNoop({
exists: () => Effect.succeed(false),
makeDirectory: () => Effect.void,
writeFileString: (path, content) => {
writes.push({ path, content })
return Effect.void
},
}),
),
)
})
it.effect("removes only stale files owned by the previous manifest", () => {
const removed: Array<string> = []
return write(
{
operations: [],
files: [{ path: "session.ts", content: "" }],
},
"/generated",
).pipe(
Effect.provideService(
FileSystem.FileSystem,
FileSystem.makeNoop({
exists: (path) => Effect.succeed(path.endsWith(".httpapi-codegen.json")),
makeDirectory: () => Effect.void,
readFileString: () => Effect.succeed('["old.ts", "session.ts"]'),
remove: (path) => {
removed.push(path)
return Effect.void
},
writeFileString: () => Effect.void,
}),
),
Effect.tap(() => Effect.sync(() => expect(removed).toEqual(["/generated/old.ts"]))),
)
})
it.effect("rejects unsafe and duplicate output paths before writing", () => {
const writes: Array<string> = []
return Effect.gen(function* () {
const error = yield* write(
{
operations: [],
files: [
{ path: "../outside.ts", content: "" },
{ path: "client.ts", content: "" },
{ path: "CLIENT.ts", content: "" },
],
},
"/generated",
).pipe(Effect.flip)
expect(error._tag).toBe("GenerationError")
expect(writes).toEqual([])
}).pipe(
Effect.provideService(
FileSystem.FileSystem,
FileSystem.makeNoop({
writeFileString: (path) => {
writes.push(path)
return Effect.void
},
}),
),
)
})
it.effect("rejects case-insensitive duplicate output paths", () => {
const writes: Array<string> = []
return Effect.gen(function* () {
const error = yield* write(
{
operations: [],
files: [
{ path: "client.ts", content: "" },
{ path: "CLIENT.ts", content: "" },
],
},
"/generated",
).pipe(Effect.flip)
expect(error._tag).toBe("GenerationError")
expect(error.reason).toBe("Duplicate output path: CLIENT.ts")
expect(writes).toEqual([])
}).pipe(
Effect.provideService(
FileSystem.FileSystem,
FileSystem.makeNoop({
writeFileString: (path) => {
writes.push(path)
return Effect.void
},
}),
),
)
})
it.effect("reserves the private manifest path", () =>
write({ operations: [], files: [{ path: ".httpapi-codegen.json", content: "" }] }, "/generated").pipe(
Effect.flip,
Effect.tap((error) => Effect.sync(() => expect(error.reason).toContain("Unsafe output path"))),
Effect.provideService(FileSystem.FileSystem, FileSystem.makeNoop({})),
),
)
it.effect("rejects existing symbolic-link output targets", () =>
write({ operations: [], files: [{ path: "session.ts", content: "" }] }, "/generated").pipe(
Effect.flip,
Effect.tap((error) => Effect.sync(() => expect(error.reason).toBe("Unsafe output path: session.ts"))),
Effect.provideService(
FileSystem.FileSystem,
FileSystem.makeNoop({
exists: (path) => Effect.succeed(path.endsWith("session.ts")),
makeDirectory: () => Effect.void,
stat: () =>
Effect.succeed({
type: "SymbolicLink",
mtime: Option.none(),
atime: Option.none(),
birthtime: Option.none(),
dev: 0,
ino: Option.none(),
mode: 0,
nlink: Option.none(),
uid: Option.none(),
gid: Option.none(),
rdev: Option.none(),
size: FileSystem.Size(0),
blksize: Option.none(),
blocks: Option.none(),
}),
}),
),
),
)
})