| 1 | import * as Schema from "effect/Schema"; |
| 2 | import { describe, expect, it } from "vite-plus/test"; |
| 3 | |
| 4 | import { fromYaml, fromYamlString } from "./schemaYaml.ts"; |
| 5 | |
| 6 | const ProjectConfig = Schema.Struct({ |
| 7 | name: Schema.String, |
| 8 | enabled: Schema.Boolean, |
| 9 | tags: Schema.Array(Schema.String), |
| 10 | }); |
| 11 | |
| 12 | describe("schemaYaml helpers", () => { |
| 13 | it("decodes YAML through a schema", () => { |
| 14 | const decodeConfig = Schema.decodeUnknownSync(fromYaml(ProjectConfig)); |
| 15 | |
| 16 | expect( |
| 17 | decodeConfig(`name: t3code |
| 18 | enabled: true |
| 19 | tags: |
| 20 | - codex |
| 21 | - effect |
| 22 | `), |
| 23 | ).toEqual({ |
| 24 | name: "t3code", |
| 25 | enabled: true, |
| 26 | tags: ["codex", "effect"], |
| 27 | }); |
| 28 | }); |
| 29 | |
| 30 | it("encodes values as YAML text", () => { |
| 31 | const encodeConfig = Schema.encodeSync(fromYaml(ProjectConfig)); |
| 32 | |
| 33 | expect( |
| 34 | encodeConfig({ |
| 35 | name: "t3code", |
| 36 | enabled: true, |
| 37 | tags: ["codex"], |
| 38 | }), |
| 39 | ).toBe(`name: t3code |
| 40 | enabled: true |
| 41 | tags: |
| 42 | - codex |
| 43 | `); |
| 44 | }); |
| 45 | |
| 46 | it("can be used as a schema transformation directly", () => { |
| 47 | const schema = Schema.String.pipe(Schema.decodeTo(Schema.Unknown, fromYamlString)); |
| 48 | const decodeYaml = Schema.decodeUnknownSync(schema); |
| 49 | |
| 50 | expect(decodeYaml("answer: 42\n")).toEqual({ answer: 42 }); |
| 51 | }); |
| 52 | |
| 53 | it("reports malformed YAML with safe structural diagnostics", () => { |
| 54 | const decodeYaml = Schema.decodeUnknownSync(fromYaml(Schema.Unknown)); |
| 55 | const secret = "credential=secret-value"; |
| 56 | let error: unknown; |
| 57 | |
| 58 | try { |
| 59 | decodeYaml(`name: ${secret}\n bad-indent: nope\n`); |
| 60 | } catch (cause) { |
| 61 | error = cause; |
| 62 | } |
| 63 | |
| 64 | expect(Schema.isSchemaError(error)).toBe(true); |
| 65 | if (!Schema.isSchemaError(error)) { |
| 66 | throw new Error("Expected a schema error"); |
| 67 | } |
| 68 | expect(error.message).toBe("Invalid YAML (code=BLOCK_AS_IMPLICIT_KEY, line=1, column=7)."); |
| 69 | expect(error.message).not.toContain(secret); |
| 70 | }); |
| 71 | |
| 72 | it("does not expose stringify failure details", () => { |
| 73 | const encodeYaml = Schema.encodeSync(fromYaml(Schema.Unknown)); |
| 74 | const secret = "credential=secret-value"; |
| 75 | let error: unknown; |
| 76 | |
| 77 | try { |
| 78 | encodeYaml({ |
| 79 | toJSON() { |
| 80 | throw new Error(secret); |
| 81 | }, |
| 82 | }); |
| 83 | } catch (cause) { |
| 84 | error = cause; |
| 85 | } |
| 86 | |
| 87 | expect(Schema.isSchemaError(error)).toBe(true); |
| 88 | if (!Schema.isSchemaError(error)) { |
| 89 | throw new Error("Expected a schema error"); |
| 90 | } |
| 91 | expect(error.message).toBe("Failed to stringify YAML."); |
| 92 | expect(error.message).not.toContain(secret); |
| 93 | }); |
| 94 | }); |