Suite of AT Protocol TypeScript libraries built on web standards
1import type { LexiconDoc } from "@atp/lexicon";
2import { Client } from "./_xrpc-client.ts";
3import * as xrpcServer from "../mod.ts";
4import { closeServer, createServer } from "./_util.ts";
5import { assertEquals, assertRejects } from "@std/assert";
6
7const LEXICONS: LexiconDoc[] = [
8 {
9 lexicon: 1,
10 id: "io.example.paramTest",
11 defs: {
12 main: {
13 type: "query",
14 parameters: {
15 type: "params",
16 required: ["str", "int", "bool", "arr"],
17 properties: {
18 str: { type: "string", minLength: 2, maxLength: 10 },
19 int: { type: "integer", minimum: 2, maximum: 10 },
20 bool: { type: "boolean" },
21 arr: { type: "array", items: { type: "integer" }, maxLength: 2 },
22 def: { type: "integer", default: 0 },
23 },
24 },
25 output: {
26 encoding: "application/json",
27 },
28 },
29 },
30 },
31];
32
33let server: ReturnType<typeof xrpcServer.createServer>;
34let s: Deno.HttpServer;
35let client: Client;
36
37Deno.test.beforeAll(async () => {
38 server = xrpcServer.createServer(LEXICONS);
39 server.method(
40 "io.example.paramTest",
41 (ctx: { params: xrpcServer.Params }) => ({
42 encoding: "application/json",
43 body: ctx.params,
44 }),
45 );
46
47 s = await createServer(server);
48 const port = (s as Deno.HttpServer & { port: number }).port;
49 client = new Client(`http://localhost:${port}`, LEXICONS);
50});
51
52Deno.test.afterAll(async () => {
53 await closeServer(s);
54});
55
56Deno.test("validates query params with valid data", {
57 sanitizeOps: false,
58 sanitizeResources: false,
59}, async () => {
60 const res1 = await client.call("io.example.paramTest", {
61 str: "valid",
62 int: 5,
63 bool: true,
64 arr: [1, 2],
65 def: 5,
66 });
67 assertEquals(res1.success, true);
68 assertEquals(res1.data.str, "valid");
69 assertEquals(res1.data.int, 5);
70 assertEquals(res1.data.bool, true);
71 assertEquals(res1.data.arr, [1, 2]);
72 assertEquals(res1.data.def, 5);
73});
74
75Deno.test("coerces query params to correct types", {
76 sanitizeOps: false,
77 sanitizeResources: false,
78}, async () => {
79 const res2 = await client.call("io.example.paramTest", {
80 str: 10,
81 int: "5",
82 bool: "foo",
83 arr: "3",
84 });
85 assertEquals(res2.success, true);
86 assertEquals(res2.data.str, "10");
87 assertEquals(res2.data.int, 5);
88 assertEquals(res2.data.bool, false);
89 assertEquals(res2.data.arr, [3]);
90 assertEquals(res2.data.def, 0);
91});
92
93Deno.test("rejects string that is too short", {
94 sanitizeOps: false,
95 sanitizeResources: false,
96}, async () => {
97 await assertRejects(
98 () =>
99 client.call("io.example.paramTest", {
100 str: "n",
101 int: 5,
102 bool: true,
103 arr: [1],
104 }),
105 Error,
106 "str must not be shorter than 2 characters",
107 );
108});
109
110Deno.test("rejects string that is too long", {
111 sanitizeOps: false,
112 sanitizeResources: false,
113}, async () => {
114 await assertRejects(
115 () =>
116 client.call("io.example.paramTest", {
117 str: "loooooooooooooong",
118 int: 5,
119 bool: true,
120 arr: [1],
121 }),
122 Error,
123 "str must not be longer than 10 characters",
124 );
125});
126
127Deno.test("rejects when required str param is missing", {
128 sanitizeOps: false,
129 sanitizeResources: false,
130}, async () => {
131 await assertRejects(
132 () =>
133 client.call("io.example.paramTest", {
134 int: 5,
135 bool: true,
136 arr: [1],
137 }),
138 Error,
139 'Params must have the property "str"',
140 );
141});
142
143Deno.test("rejects integer that is too small", {
144 sanitizeOps: false,
145 sanitizeResources: false,
146}, async () => {
147 await assertRejects(
148 () =>
149 client.call("io.example.paramTest", {
150 str: "valid",
151 int: -1,
152 bool: true,
153 arr: [1],
154 }),
155 Error,
156 "int can not be less than 2",
157 );
158});
159
160Deno.test("rejects integer that is too large", {
161 sanitizeOps: false,
162 sanitizeResources: false,
163}, async () => {
164 await assertRejects(
165 () =>
166 client.call("io.example.paramTest", {
167 str: "valid",
168 int: 11,
169 bool: true,
170 arr: [1],
171 }),
172 Error,
173 "int can not be greater than 10",
174 );
175});
176
177Deno.test("rejects when required int param is missing", {
178 sanitizeOps: false,
179 sanitizeResources: false,
180}, async () => {
181 await assertRejects(
182 () =>
183 client.call("io.example.paramTest", {
184 str: "valid",
185 bool: true,
186 arr: [1],
187 }),
188 Error,
189 'Params must have the property "int"',
190 );
191});
192
193Deno.test("rejects when required bool param is missing", {
194 sanitizeOps: false,
195 sanitizeResources: false,
196}, async () => {
197 await assertRejects(
198 () =>
199 client.call("io.example.paramTest", {
200 str: "valid",
201 int: 5,
202 arr: [1],
203 }),
204 Error,
205 'Params must have the property "bool"',
206 );
207});
208
209Deno.test("rejects when required array param is empty", {
210 sanitizeOps: false,
211 sanitizeResources: false,
212}, async () => {
213 await assertRejects(
214 () =>
215 client.call("io.example.paramTest", {
216 str: "valid",
217 int: 5,
218 bool: true,
219 arr: [],
220 }),
221 Error,
222 'Error: Params must have the property "arr"',
223 );
224});
225
226Deno.test("rejects array that exceeds max length", {
227 sanitizeOps: false,
228 sanitizeResources: false,
229}, async () => {
230 await assertRejects(
231 () =>
232 client.call("io.example.paramTest", {
233 str: "valid",
234 int: 5,
235 bool: true,
236 arr: [1, 2, 3],
237 }),
238 Error,
239 "Error: arr must not have more than 2 elements",
240 );
241});