format.test.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import { NodeChildProcessSpawner, NodeFileSystem, NodePath } from "@effect/platform-node"
  2. import { describe, expect } from "bun:test"
  3. import { Effect, Layer } from "effect"
  4. import { provideTmpdirInstance } from "../fixture/fixture"
  5. import { testEffect } from "../lib/effect"
  6. import { Format } from "../../src/format"
  7. import { Config } from "../../src/config/config"
  8. import * as Formatter from "../../src/format/formatter"
  9. const node = NodeChildProcessSpawner.layer.pipe(
  10. Layer.provideMerge(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)),
  11. )
  12. const it = testEffect(Layer.mergeAll(Format.layer, node).pipe(Layer.provide(Config.defaultLayer)))
  13. describe("Format", () => {
  14. it.effect("status() returns built-in formatters when no config overrides", () =>
  15. provideTmpdirInstance(() =>
  16. Format.Service.use((fmt) =>
  17. Effect.gen(function* () {
  18. const statuses = yield* fmt.status()
  19. expect(Array.isArray(statuses)).toBe(true)
  20. expect(statuses.length).toBeGreaterThan(0)
  21. for (const item of statuses) {
  22. expect(typeof item.name).toBe("string")
  23. expect(Array.isArray(item.extensions)).toBe(true)
  24. expect(typeof item.enabled).toBe("boolean")
  25. }
  26. const gofmt = statuses.find((item) => item.name === "gofmt")
  27. expect(gofmt).toBeDefined()
  28. expect(gofmt!.extensions).toContain(".go")
  29. }),
  30. ),
  31. ),
  32. )
  33. it.effect("status() returns empty list when formatter is disabled", () =>
  34. provideTmpdirInstance(
  35. () =>
  36. Format.Service.use((fmt) =>
  37. Effect.gen(function* () {
  38. expect(yield* fmt.status()).toEqual([])
  39. }),
  40. ),
  41. { config: { formatter: false } },
  42. ),
  43. )
  44. it.effect("status() excludes formatters marked as disabled in config", () =>
  45. provideTmpdirInstance(
  46. () =>
  47. Format.Service.use((fmt) =>
  48. Effect.gen(function* () {
  49. const statuses = yield* fmt.status()
  50. const gofmt = statuses.find((item) => item.name === "gofmt")
  51. expect(gofmt).toBeUndefined()
  52. }),
  53. ),
  54. {
  55. config: {
  56. formatter: {
  57. gofmt: { disabled: true },
  58. },
  59. },
  60. },
  61. ),
  62. )
  63. it.effect("service initializes without error", () =>
  64. provideTmpdirInstance(() => Format.Service.use(() => Effect.void)),
  65. )
  66. it.effect("status() initializes formatter state per directory", () =>
  67. Effect.gen(function* () {
  68. const a = yield* provideTmpdirInstance(() => Format.Service.use((fmt) => fmt.status()), {
  69. config: { formatter: false },
  70. })
  71. const b = yield* provideTmpdirInstance(() => Format.Service.use((fmt) => fmt.status()))
  72. expect(a).toEqual([])
  73. expect(b.length).toBeGreaterThan(0)
  74. }),
  75. )
  76. it.effect("runs enabled checks for matching formatters in parallel", () =>
  77. provideTmpdirInstance((path) =>
  78. Effect.gen(function* () {
  79. const file = `${path}/test.parallel`
  80. yield* Effect.promise(() => Bun.write(file, "x"))
  81. const one = {
  82. extensions: Formatter.gofmt.extensions,
  83. enabled: Formatter.gofmt.enabled,
  84. command: Formatter.gofmt.command,
  85. }
  86. const two = {
  87. extensions: Formatter.mix.extensions,
  88. enabled: Formatter.mix.enabled,
  89. command: Formatter.mix.command,
  90. }
  91. let active = 0
  92. let max = 0
  93. yield* Effect.acquireUseRelease(
  94. Effect.sync(() => {
  95. Formatter.gofmt.extensions = [".parallel"]
  96. Formatter.mix.extensions = [".parallel"]
  97. Formatter.gofmt.command = ["sh", "-c", "true"]
  98. Formatter.mix.command = ["sh", "-c", "true"]
  99. Formatter.gofmt.enabled = async () => {
  100. active++
  101. max = Math.max(max, active)
  102. await Bun.sleep(20)
  103. active--
  104. return true
  105. }
  106. Formatter.mix.enabled = async () => {
  107. active++
  108. max = Math.max(max, active)
  109. await Bun.sleep(20)
  110. active--
  111. return true
  112. }
  113. }),
  114. () =>
  115. Format.Service.use((fmt) =>
  116. Effect.gen(function* () {
  117. yield* fmt.init()
  118. yield* fmt.file(file)
  119. }),
  120. ),
  121. () =>
  122. Effect.sync(() => {
  123. Formatter.gofmt.extensions = one.extensions
  124. Formatter.gofmt.enabled = one.enabled
  125. Formatter.gofmt.command = one.command
  126. Formatter.mix.extensions = two.extensions
  127. Formatter.mix.enabled = two.enabled
  128. Formatter.mix.command = two.command
  129. }),
  130. )
  131. expect(max).toBe(2)
  132. }),
  133. ),
  134. )
  135. it.effect("runs matching formatters sequentially for the same file", () =>
  136. provideTmpdirInstance(
  137. (path) =>
  138. Effect.gen(function* () {
  139. const file = `${path}/test.seq`
  140. yield* Effect.promise(() => Bun.write(file, "x"))
  141. yield* Format.Service.use((fmt) =>
  142. Effect.gen(function* () {
  143. yield* fmt.init()
  144. yield* fmt.file(file)
  145. }),
  146. )
  147. expect(yield* Effect.promise(() => Bun.file(file).text())).toBe("xAB")
  148. }),
  149. {
  150. config: {
  151. formatter: {
  152. first: {
  153. command: ["sh", "-c", 'sleep 0.05; v=$(cat "$1"); printf \'%sA\' "$v" > "$1"', "sh", "$FILE"],
  154. extensions: [".seq"],
  155. },
  156. second: {
  157. command: ["sh", "-c", 'v=$(cat "$1"); printf \'%sB\' "$v" > "$1"', "sh", "$FILE"],
  158. extensions: [".seq"],
  159. },
  160. },
  161. },
  162. },
  163. ),
  164. )
  165. })