| 1 | import { assert, describe, it } from "@effect/vitest" |
| 2 | import { Context } from "effect" |
| 3 | import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/http-api" |
| 4 | |
| 5 | describe("HttpApi", () => { |
| 6 | it("stores the supplied identifier", () => { |
| 7 | const api = HttpApi.make("Api") |
| 8 | |
| 9 | assert.strictEqual(api.identifier, "Api") |
| 10 | }) |
| 11 | |
| 12 | it("initializes groups as a readonly record", () => { |
| 13 | const api = HttpApi.make("Api") |
| 14 | |
| 15 | assert.deepStrictEqual(api.groups, {}) |
| 16 | }) |
| 17 | |
| 18 | it("stores __proto__ group and endpoint identifiers as own properties", () => { |
| 19 | const group = HttpApiGroup.make("__proto__").add( |
| 20 | HttpApiEndpoint.get("__proto__", "/proto") |
| 21 | ) |
| 22 | const child = HttpApi.make("Child").add(group) |
| 23 | const api = HttpApi.make("Api").addHttpApi(child) |
| 24 | |
| 25 | assert.isTrue(Object.hasOwn(group.endpoints, "__proto__")) |
| 26 | assert.isTrue(Object.hasOwn(child.groups, "__proto__")) |
| 27 | assert.isTrue(Object.hasOwn(api.groups, "__proto__")) |
| 28 | assert.isTrue(Object.hasOwn(api.groups["__proto__"].endpoints, "__proto__")) |
| 29 | |
| 30 | const reflected: Array<string> = [] |
| 31 | HttpApi.reflect(api, { |
| 32 | onGroup: ({ group }) => reflected.push(group.identifier), |
| 33 | onEndpoint: ({ endpoint }) => reflected.push(endpoint.identifier) |
| 34 | }) |
| 35 | assert.deepStrictEqual(reflected, ["__proto__", "__proto__"]) |
| 36 | }) |
| 37 | |
| 38 | it("does not mutate groups from the added API", () => { |
| 39 | const group = HttpApiGroup.make("users").annotate(OpenApi.Title, "Users") |
| 40 | const child = HttpApi.make("Child") |
| 41 | .annotate(OpenApi.Description, "Child API") |
| 42 | .add(group) |
| 43 | const originalAnnotations = group.annotations |
| 44 | |
| 45 | const parent = HttpApi.make("Parent").addHttpApi(child) |
| 46 | |
| 47 | assert.strictEqual(group.annotations, originalAnnotations) |
| 48 | assert.strictEqual(child.groups.users, group) |
| 49 | assert.notStrictEqual(parent.groups.users, group) |
| 50 | assert.strictEqual(Context.getUnsafe(parent.groups.users.annotations, OpenApi.Title), "Users") |
| 51 | assert.strictEqual(Context.getUnsafe(parent.groups.users.annotations, OpenApi.Description), "Child API") |
| 52 | }) |
| 53 | |
| 54 | it("does not add inherited groups", () => { |
| 55 | const inherited = HttpApiGroup.make("inherited") |
| 56 | const child = HttpApi.make("Child").add(HttpApiGroup.make("own")) |
| 57 | Object.setPrototypeOf(child.groups, { inherited }) |
| 58 | |
| 59 | const parent = HttpApi.make("Parent").addHttpApi(child) |
| 60 | |
| 61 | assert.isTrue(Object.hasOwn(parent.groups, "own")) |
| 62 | assert.isFalse(Object.hasOwn(parent.groups, "inherited")) |
| 63 | }) |
| 64 | |
| 65 | it("keeps annotations from API variants isolated", () => { |
| 66 | const group = HttpApiGroup.make("users").annotate(OpenApi.Title, "Users") |
| 67 | const child = HttpApi.make("Child").add(group) |
| 68 | const publicChild = child.annotate(OpenApi.Description, "Public API") |
| 69 | const internalChild = child.annotate(OpenApi.Description, "Internal API") |
| 70 | |
| 71 | const publicParent = HttpApi.make("PublicParent").addHttpApi(publicChild) |
| 72 | const internalParent = HttpApi.make("InternalParent").addHttpApi(internalChild) |
| 73 | |
| 74 | assert.strictEqual(Context.getUnsafe(publicParent.groups.users.annotations, OpenApi.Description), "Public API") |
| 75 | assert.strictEqual(Context.getUnsafe(internalParent.groups.users.annotations, OpenApi.Description), "Internal API") |
| 76 | assert.notStrictEqual(publicParent.groups.users, internalParent.groups.users) |
| 77 | |
| 78 | const publicTag = OpenApi.fromApi(publicParent).tags.find((tag) => tag.name === "Users") |
| 79 | const internalTag = OpenApi.fromApi(internalParent).tags.find((tag) => tag.name === "Users") |
| 80 | assert.isDefined(publicTag) |
| 81 | assert.isDefined(internalTag) |
| 82 | assert.strictEqual(publicTag.description, "Public API") |
| 83 | assert.strictEqual(internalTag.description, "Internal API") |
| 84 | }) |
| 85 | |
| 86 | it("preserves group annotation precedence", () => { |
| 87 | const group = HttpApiGroup.make("users").annotate(OpenApi.Title, "Group title") |
| 88 | const child = HttpApi.make("Child") |
| 89 | .annotate(OpenApi.Title, "API title") |
| 90 | .add(group) |
| 91 | |
| 92 | const parent = HttpApi.make("Parent").addHttpApi(child) |
| 93 | |
| 94 | assert.strictEqual(Context.getUnsafe(parent.groups.users.annotations, OpenApi.Title), "Group title") |
| 95 | }) |
| 96 | }) |